# Agents
Source: https://docs.supaboard.ai/agents/overview
Configure an assistant with the data access and business knowledge your team needs.
An **Agent** is an AI assistant you configure to understand your data. It knows which tables it can query, what the data means, how to interpret your business terminology, and what rules to enforce. Once set up it powers [the Analyst](/analyst/overview), so your team can get accurate answers in plain English.
A good agent is not a one-off setup. It has a confidence score you can watch, an evaluation suite you can run, memory it accumulates from real conversations, and skills you teach it — all of which you tune over time.
## Before you begin
Connect the relevant data sources and gather the business definitions the agent should follow. You need Agents feature access and permission to create or configure an agent.
Start with [Set up an agent for your team](/agents/set-up-an-agent), then use this page as the reference for each setting.
## Browsing Agents
Navigate to **Agents** in the sidebar to see every agent in your workspace.
### Display modes
Toggle between two views using the switcher in the top-right:
| View | What you see |
| - | - |
| **Card grid** | Visual cards with agent name, description, and data source icons |
| **Table** | Name · Description · Resource · Created By · Created On · Actions |
## Creating an Agent
Click **New Agent**. This opens a full creation page — *"Let's create your new agent"* — with a live preview of the agent card beside the form.
| Field | Required | Notes |
| - | - | - |
| **Agent Name** | Yes | Human-readable label shown across the UI |
| **Select Agent Look** | No | Pick an avatar for the agent |
| **Data Source Access** | Yes (at least one) | The connections this agent may query. Inactive connections cannot be selected. |
| **User Access** | No | Which workspace members can use this agent |
> There is no Description field here. The description is filled in for you if you use **Tune with AI**, and you can edit it later from **Who am I**.
Then choose how to continue:
| Button | Where it takes you |
| - | - |
| **Build Manually** | The agent's home page, where you configure everything yourself |
| **Tune with AI** | A guided [setup questionnaire](#the-setup-questionnaire) that configures the agent from your answers |
## Agent Home
Opening an agent lands you on its home page — a dashboard of how well the agent is doing and what to fix next.
### Header
| Control | What it does |
| - | - |
| **Try in Chat** | Opens a conversation with this agent so you can test a change immediately |
| **Tune with AI** | Opens the [tuning assistant](#the-tuning-assistant) side panel |
### Cards
| Card | What it shows |
| - | - |
| **Confidence Score** | A gauge of the agent's current accuracy, a trend line over recent evaluations, and a **Re-evaluate** button. Reads *"Not enough evals in the last 7 days yet"* until you've run some. |
| **Issues** | Problems found by evaluation, each with **Mark as fixed** / **Reopen** |
| **Quick Actions** | **Self Improve** and **AI Insights** toggles (the same settings as **Enable Self-Improvement** and **Enable Data Access** on Who am I), plus Slack and Teams tiles |
> The Slack and Teams tiles on this page are not wired yet — clicking either shows a placeholder message. Slack is connected from Slack itself, not from here: see [Connect Supaboard to Slack](/analyst/integrations/connect-supaboard-to-slack). Teams is not available.
### Fine Tuning Setup
A grid of seven tiles that doubles as the agent's navigation and its to-do list. Tiles carrying open issues are flagged.
| Tile | Purpose |
| - | - |
| **Who am I** | Identity, data access, and controls |
| **Knowledge Base** | Your business tribal knowledge |
| **Memory** | What the agent has learned from past conversations |
| **Rules** | Guardrails followed on every query |
| **Table Configuration** | Context on your data so queries land right |
| **Verified Queries** | Answers you've signed off on |
| **Skills** | Reusable procedures the agent loads on demand |
Below that, the latest evaluation run is summarised.
## Navigating an Agent
There is no tab bar. Use the **breadcrumb dropdown** in the topbar to move between sections, or the Fine Tuning Setup tiles on the home page.
| Section | What it covers |
| - | - |
| **Who am I** | Name, description, image, data scope, overview, access controls |
| **Knowledge Base** | Uploaded reference documents, broken into searchable chunks |
| **Memory** | Facts the agent has retained from real conversations |
| **Rules** | Hard rules the agent must follow |
| **Table Configuration** | Per-table business context |
| **Verified Queries** | Pre-approved SQL for known questions |
| **Evaluation** | Test suites and run history |
| **Skills** | Packaged instructions for particular kinds of task |
> If you used Supaboard before mid-2026: **Setup** is now **Who am I**, **Master Ruleset** is now **Rules**, and **Fine Tuning** is now **Verified Queries**.
### Sharing an agent
A share icon in the topbar opens **Share `{agent}`**, where you invite workspace members to this agent's access list. Collaborators appear as a stack of avatars beside it. There is no public link — agent sharing is workspace-members-only.
## Who am I
### Information
| Field | Details |
| - | - |
| **Name** | Rename the agent at any time |
| **Description** | The summary shown in the agents list |
| **Image** | Upload a custom image (max 32 MB). Supaboard extracts the dominant colour for theming. Requires the custom agent image feature on your plan — see [pricing](https://supaboard.ai/pricing). |
### Data source access
Controls exactly which data the agent can see.
* **Multi-select dropdown** — add or remove data source connections
* **Table-level scope** — expand any connected source to pick specific tables. All tables are included by default.
* A count badge shows sources and tables at a glance
#### Handling missing schema
If a table's schema hasn't been generated, a banner reads *"Some tables have missing schema"* with a **Go to Table Configuration** link. Fix it there with **Regenerate Schema**.
> A missing schema means the agent doesn't know what the columns in that table mean. Resolve schema warnings before expecting accurate answers.
### Overview
The agent's core instruction set — a freeform text block that tells it:
* What this data represents
* The business domain it operates in
* How to interpret ambiguous questions
* Any context about data quirks or conventions
Domain-specific **quick templates** are offered based on the data sources you've connected. Click one to append its suggested language.
> The overview is the single most impactful thing you can write for an agent. A detailed, precise overview produces dramatically better answers than a vague one.
### Access and controls
#### Enable Data Access
When on, the agent can drill into the actual data behind charts for deeper analysis and follow-up insights. When off, responses stay at the aggregated level. **On by default.** Mirrored on Agent Home as **AI Insights**.
#### Enable Self-Improvement
When on, the agent updates table descriptions, creates new metrics and dimensions from conversation patterns, and refines its understanding over time. **On by default.** Mirrored on Agent Home as **Self Improve**.
#### User Access
Controls which workspace members can chat with this agent.
* **All users** (default) — every workspace member with Ask Analysts access
* **Specific users** — only the selected people. The workspace **Owner** is always included and cannot be removed.
The dropdown lists all workspace members with their roles; your own entry is marked *(you)*.
### Danger Zone
**Remap Data Source** repoints the agent at a different data source. It warns that *"This will delete all the changes done before in the data source"* — table configuration, schema context, and anything else tied to the old source is discarded. Confirmation is required.
## Knowledge Base
The knowledge base gives the agent context beyond the database — data dictionaries, business definitions, policy documents, or any reference material.
### Uploading files
Click **Upload** or drag files onto the page. Supported formats include PDF, CSV, Excel (`.xlsx` / `.xls`), Word, plain text, and images. Multiple files can be selected at once.
Each file is split into **chunks** — the fragments the agent actually retrieves when answering.
### Browsing what's stored
Files are listed as rows showing `{n} chunks · {TYPE}`. Expand a row to read the chunks inline, or open the file for the full chunk-by-chunk view. Each chunk is labelled so you can see exactly what the agent will retrieve.
Use **Search knowledge chunks** to search across the *contents* of every chunk, not just file names — useful for checking whether a specific definition actually made it in.
Delete a file from its row menu (**Delete file**); the change takes effect immediately.
## Memory
Memory is what the agent has retained from real conversations. Unlike the knowledge base, you don't write it — it accumulates on its own, primarily from feedback on answers.
| Column | Description |
| - | - |
| **Memory Context** | The retained fact |
| **Entities** | People, metrics, or tables the memory refers to |
| **Tags** | Categorisation |
| **Sources** | Where the memory came from |
| **Created By** | Who triggered it |
| **Updated At** | Last change |
| **Action** | Row menu |
Click any row to open **Memory Details**, showing the **Extracted Memory** in full with **Copy** and **Delete**.
> Memory is captured in the background — there is nothing to run or schedule. Review it periodically and delete anything wrong: a bad memory is worse than no memory, because the agent will keep applying it.
## Rules
Hard rules that govern how the agent interprets and answers every query. These are not suggestions.
Use rules to:
* Enforce terminology — *"always call this field 'Net Revenue', not 'Revenue'"*
* Define business logic — *"always filter to the current fiscal year unless the user specifies otherwise"*
* Set formatting standards — *"always express percentages to 2 decimal places"*
* Prevent certain response types
### Managing rules
**Create a rule manually:** click **New Rule**, enter a **Title** (short label) and **Description** (the rule in plain language), and save.
**Import rules from a file:** click **Browse File** and upload a document — `.txt`, `.md`, `.doc`, `.docx`, `.pdf`, `.csv`, `.xlsx`, `.xls`, `.json`, or an image. Supaboard extracts rules from it automatically.
**Edit or delete:** use the row's **Edit** and **Delete** actions.
**Search** rules using the search bar above the table.
## Table Configuration
Teach the agent what each table *means* — context a SQL schema alone cannot convey.
### Navigating
The left sidebar is a tree of your data sources and their tables. Tables outside the agent's scope appear dimmed. Select any table to edit its context.
### Editing
Context is written as **Markdown** in a single editor with **Edit** and **Preview** modes. Cover, at minimum:
| Topic | What to write |
| - | - |
| **Summary** | What this table stores, and its key relationships to other tables |
| **Granularity** | What one row represents — *"One row per order line item"*, *"One row per daily user session"* |
| **Gotchas** | Known quirks and caveats — *"The `deleted_at` column is not reliably populated before 2022"* |
| **Use cases** | Common questions this table helps answer, so the agent knows when to reach for it |
**Regenerate Schema** re-scans the selected table and refreshes its column metadata. Use it after a schema change upstream, or to clear a missing-schema warning.
> Table Configuration is the second most impactful investment after the Overview. Granularity and Gotchas in particular prevent the most common categories of wrong answer.
## Verified Queries
Pre-written, validated SQL the agent reuses for known questions. When someone asks something that closely matches a verified query, the agent uses it directly instead of writing a new one. That guarantees:
* Correctness for high-stakes metrics
* Consistent answers across everyone who asks
* Faster responses — no generation step
### Adding one
Click **New Query** (or **Create Query** on the empty state). The editor has a **Title** and a SQL pane; click **Save & Run Query** to validate it against your data and save.
### Managing them
Select a row to open it in the editor. The row menu offers **Delete**.
### Automatic promotion
When someone gives an answer a thumbs up in the Analyst, the query behind it is promoted into this list automatically. Promoted entries link back to the conversation they came from, so you can see the question that produced them.
> Review promoted queries periodically. They're a good source of coverage, but a thumbs up means "this looked right to one person", not "this is the canonical definition".
## Evaluation
Evaluation is how you find out whether your configuration actually works, and it drives the Confidence Score on Agent Home.
| Tab | Contents |
| - | - |
| **Evals** | Your test cases. **New Evaluation** adds one. |
| **Run History** | Past runs. **Run Evaluation** starts a new one; the button reads *Running…* while in progress. |
Results stream in live — a banner tracks *Running evaluation* through to *Evaluation complete*. Open any run for a per-change detail panel.
### Issues carry forward
Issues the last run found and you haven't marked as fixed are **re-verified in the next run**. You don't have to remember what was outstanding — fix something, re-run, and the run tells you whether it's actually resolved.
## Skills
Skills are packaged instructions that shape how an agent handles a particular kind of task — a reporting convention, a recurring analysis, a house style for a specific question.
### Creating a skill
**New Skill** offers three routes:
| Route | What happens |
| - | - |
| **Create with AI** | Describe what you want and refine it through chat |
| **Write Skill** | Fill in **Name**, **Description**, **Instructions**, and a **Trigger** (Automatic or Manual) |
| **Upload Skill** | Upload an existing skill file |
#### Upload requirements
Accepted extensions are `.md`, `.zip`, and `.skill`.
* A `.md` file must contain the skill name and description formatted in YAML
* A `.zip` or `.skill` file must include a `SKILL.md`
### Triggers
| Trigger | Behaviour |
| - | - |
| **Automatic** | The agent decides when the skill is relevant and loads it on its own |
| **Manual** | The skill is applied only when explicitly invoked |
### Managing skills
| Column | Description |
| - | - |
| **Skill** | Name and description |
| **Status** | Active or Disabled |
| **Source** | AI, Text, or File — how the skill was created |
| **Trigger** | Automatic or Manual |
| **Created By** | Who added it |
| **Updated At** | Last change |
| **Action** | Row menu |
Row actions: **Try in Chat**, **Edit**, **Edit with AI**, **Delete**. Open a skill to see its full markdown in a detail panel, where a switch enables or disables it.
Search with **Search skills**, narrow with **Filter** (Status, Source, Trigger), and reorder with **Sort** (Last updated or Name).
### Agents managing their own skills
While chatting with an agent you can ask it to list, create, edit, or delete its own skills — *"add a skill that always reports revenue net of refunds"*. Changes are attributed so you can see what the agent changed versus what a person did.
## Building With AI
### The setup questionnaire
Choosing **Tune with AI** when creating an agent opens a guided questionnaire. Supaboard analyses your connected data, then walks you through category-grouped questions — *"Question 3 of 8"*, each tagged with the area it improves.
For each question, pick an option, type your own answer, or **Skip**. When you finish, your answers are applied and the agent's description is written for you.
> One option in each question is prefixed with **(Recommended)** in its text. There's no badge or highlight — read the option text.
### The tuning assistant
**Tune with AI** on Agent Home opens a chat panel beside your work. Describe how the agent should behave — *"Tell the agent how it should behave…"* — and it reads and updates the agent's configuration for you, showing its reasoning as it goes. It accepts file attachments, and past tuning sessions are kept under **Recent** in the session picker.
#### Undoing a change
Every change the assistant makes is captured beforehand, and it hands you an **undo token** in the chat. To roll back, ask it to — *"undo that last change"*. There is no undo button; the rollback is conversational.
## Managing Agents
### Duplicate
Open the action menu (three dots) on any agent and select **Duplicate**. You'll be asked for a new name. The duplicate copies the full configuration: data scope, overview, knowledge, rules, table config, verified queries, and skills.
### Delete
Action menu → **Delete**, with confirmation. Deletion is permanent. Existing conversations that used the agent are preserved but can no longer produce new responses from it.
### Unsaved changes
Draft edits are held in your browser for the current tab and restored if you navigate back within the session. They are **not** shareable by link and do not survive opening the agent in a new tab, so save before you close it. Closing or reloading the browser with unsaved edits prompts you first.
### The default agent
Every workspace gets an agent named **Stella** automatically, created the first time a data source finishes syncing. It's scoped to General and can be selected in the Analyst; you can create and choose other agents for different tasks.
## Permissions
| Action | Minimum role |
| - | - |
| Open the Agents section | Editor |
| Create an agent | Editor |
| Edit agent configuration | Editor |
| Duplicate an agent | Editor |
| Delete an agent | Editor |
| Use the tuning assistant | Editor |
Access to the Agents section is also controlled by the **Agents** feature flag on each user's workspace membership — see [Workspace — Feature Access](/workspace/overview#feature-access).
Agent-level access is separate from workspace role: even an Admin can be excluded from a specific agent if it has a restricted **User Access** list — see [Access and controls](#access-and-controls).
> Deleting an agent is not gated above Editor. Anyone who can reach the Agents section can delete an agent, so scope the **Agents** feature flag accordingly.
## Agent Setup Checklist
To get a new agent to production quality:
* Connect the right data sources and scope down to relevant tables only
* Write a detailed **Overview** — business domain, data conventions, known ambiguities
* Fill in **Table Configuration** for every table, especially Granularity and Gotchas
* Upload a **Knowledge Base** of data dictionaries and reference documents
* Define **Rules** for business-critical terminology and logic
* Add **Verified Queries** for your 5–10 most frequently asked questions
* Add **Skills** for recurring tasks that need a consistent approach
* Set **User Access** to the right people
* Run an **Evaluation**, fix what it finds, and re-run until the Confidence Score holds
* Check **Memory** periodically and delete anything the agent got wrong
# Set up an agent for your team
Source: https://docs.supaboard.ai/agents/set-up-an-agent
Give an agent the data access, definitions, and examples it needs for reliable answers.
Create an agent for a subject your team asks about regularly, such as revenue or customer success. Its data scope and business knowledge help the Analyst answer those questions consistently.
## Before you begin
You need access to **Agents**, permission to create an agent, and at least one active data source. Gather important metric definitions, exclusions, and example questions with known answers.
## 1. Create the agent
Open **Agents → New Agent**. Enter an **Agent Name**, choose its look, select **Data Source Access**, and review **User Access**.
Choose **Tune with AI** for guided setup, or **Build Manually** to configure the agent yourself.
## 2. Explain your business
In **Who am I**, describe the agent's purpose and the business context it needs. Use **Table Configuration** for table meanings and known gotchas. Upload reference documents to **Knowledge Base**.
Be specific. “Revenue excludes cancelled orders and uses the order date” is more useful than “Give accurate revenue answers.” Confirm that this definition matches your business before using it.
## 3. Add rules and examples
Use **Rules** for instructions the agent should follow consistently. Add **Verified Queries** for important questions whose answers you have checked. Keep source permissions limited to the data relevant to the agent's purpose.
## 4. Test the agent
Choose **Try in Chat** and ask a small set of questions with known answers. Include a follow-up and a question with an ambiguous definition. Check both the result and the explanation.
Run an **Evaluation** when configured, inspect the issues, and correct the underlying definitions or examples before testing again.
## 5. Make it available
Review **User Access** so the intended teammates can select the agent in the Analyst. If someone cannot use it, check their workspace feature access and the agent's access list.
## Expected result
Your teammates can select the agent, and its answers use the intended data scope and definitions. Keep reviewing feedback and retained memory as your data changes.
## Next steps
Use the [agent reference](/agents/overview) for each configuration area, or [connect MCP tools](/agents/tools) for access to an external service.
# MCP & Tools
Source: https://docs.supaboard.ai/agents/tools
Connect external services and choose the tools your agents may use.
MCP connections let an agent use an external service while working on a task. A server can provide several tools, such as searching documents or looking up a record. You choose the connection, authorization, and tools available to the agent.
For asking Supaboard questions from another app, see [Claude](/analyst/integrations/connect-supaboard-to-claude) or [Slack](/analyst/integrations/connect-supaboard-to-slack).
## Before you begin
You need access to **Settings → MCP & Tools** and permission to add connections. For a custom server, obtain its MCP endpoint, transport, and authentication requirements from its administrator. An ordinary website URL or REST API URL is not necessarily an MCP endpoint.
## The MCP & Tools Page
Open **Settings → MCP & Tools**. The list shows your available connections and their status. Open a connection to inspect its tools and usage history.
### Display modes
Use the grid or table view to browse connections.
### Search
Search by connection name when the list grows.
### Refresh
Use **Refresh** to reload the list or a server's tools after a configuration change.
## Adding a Tool
Choose **New Tool**, or **Add First Tool** in an empty workspace. The **Connectors** catalog opens. Pick the service you want and follow its connection flow, or choose **Add** to open **Custom MCP server**.
### Required fields
For a custom server:
1. Give it a recognizable **Name**.
2. Enter its MCP endpoint under **Connection**.
3. Select **Streamable HTTP** or **SSE** to match the server.
4. Choose **Authentication**: **None**, **API token**, or **OAuth**.
5. Complete the required authentication details, review the acknowledgement in the form, and choose **Create**.
### Optional fields
Use **Description** to explain when the agent should use the connection. You can also choose an icon. OAuth servers can require advanced settings supplied by the provider, such as a client ID, client secret, or scopes. Copy the callback URL shown by Supaboard when configuring that provider.
### What happens when you save
The connection is added and its status indicates whether it is ready. For OAuth, complete the provider's authorization step. If you skip or cancel sign-in, the connection still needs authorization before it can be used.
For API-token authentication, the token is sent as a Bearer credential and is stored server-side. Use the connection's edit or credentials controls when it needs updating.
## Transport Types: HTTP vs SSE
| Transport | Choose it when |
| - | - |
| **Streamable HTTP** | The MCP server supplies a Streamable HTTP endpoint |
| **SSE** | The MCP server supplies an SSE endpoint |
Use the server provider's documented transport and URL. Choosing a transport does not turn an unrelated HTTP endpoint into an MCP server.
## Tool Detail & Execution History
The connection detail page has expandable **Tools** and usage-history sections.
### Choose the tools agents can use
Review each tool's description. Switch off tools that should not be available to the agent. A disabled tool is excluded from its available tool list.
If the server cannot be reached, Supaboard may show its last known tool list with a warning. Refresh it after resolving the connection issue.
### Review usage history
Open a history entry to inspect the invocation and its result. Use this when checking whether the agent called the expected tool and whether it succeeded. Do not treat an old successful call as confirmation that the connection is working now.
## Testing Connectivity
Open the connection's action menu and choose **Test Connectivity**. Read the success or error message. If it fails, check the endpoint, authorization, and server availability.
A successful connectivity check confirms the connection can be reached; it does not validate every action exposed by the server.
## Access Control
For connections that support sharing, use **Access Control** to choose which workspace members may use them, then save the changes. Personal connectors act under the account that authorized them and do not offer this sharing action.
A teammate also needs the appropriate workspace feature access. The external service's permissions continue to apply.
## Use the connection in a chat
Open [the Analyst](/analyst/overview), use the **+** menu, and select the connected tool. Ask a specific question that needs that service. Review the tool's result and any action requesting approval.
## Deleting a Tool
Choose **Delete Tool** from the action menu and read the confirmation. Deleting removes the connection and its usage history; agents using it lose access to its tools.
## Permissions
Your workspace's **MCP and Tools** feature access controls whether this section is available. Management controls also depend on your role and the connection. Ask an administrator if a required action is missing.
## Expected result
The connection is authorized, the intended tools are enabled, and the agent can use it in a relevant chat. Return to its history to investigate failures or unexpected results.
## Next steps
[Set up an agent](/agents/set-up-an-agent) or [ask the Analyst](/analyst/overview) using the new connection.
# Understand and refine answers
Source: https://docs.supaboard.ai/analyst/insights-in-ask-agents
Read the explanation, check the supporting data, and ask useful follow-up questions.
An answer can combine a written explanation with charts, tables, headline metrics, and generated artifacts. Start with the conclusion, then inspect the evidence that matters to your decision.
## Before you begin
[Ask the Analyst](/analyst/overview) a question using an agent with access to the relevant data. Include any specific definition or comparison you have in mind.
## Read the answer
1. Check the **scope**: the date range, filters, and groups included.
2. Check the **meaning**: what counts as revenue, an active customer, or another business metric.
3. Open the supporting **chart or table** to examine the numbers.
4. Look for **assumptions and missing information** before acting on the result.
Ask follow-ups in the same chat: “Does revenue include refunds?” or “Show the same comparison for returning customers only.”
## Choose the right depth
The current modes are **Flash**, **Pro**, and **Max**. Flash suits everyday questions; Pro supports deeper analysis; Max plans before investigating complex tasks. Older guides refer to Concise and Deep Dive.
Request the detail you need in any mode: “Give me three key findings” or “Explain the drivers and show the supporting breakdowns.” A shorter explanation still needs the right data and definitions.
## Work with the result
* **Open** a visual or artifact for a closer look.
* Use a widget's **⋯** menu for actions such as downloading an image or exporting a table as CSV.
* Use **Pin** to reuse a widget in a dashboard or Datasite.
* Ask for a change in the same chat: “Use a line chart” or “Add a breakdown by channel.”
## Correct a result
Use **Dislike** on the affected widget and explain what was wrong when prompted—for example, “Exclude cancelled orders.” Use **Like** for a useful result.
If the issue is a business definition everyone should use, update the agent's [rules, knowledge, or verified queries](/agents/overview) to provide shared context for future questions.
## Next steps
You should now know what the answer covers and which data supports it. [Improve your next prompt](/analyst/interacting-with-ai), [create a report](/artifacts/reports-and-presentations), or [save the work in a project](/projects/overview).
# Connect Supaboard to Claude
Source: https://docs.supaboard.ai/analyst/integrations/connect-supaboard-to-claude
Supaboard is available as a **connector for Claude**. Add it once, and you can ask questions about your dashboards and data from inside a Claude conversation — without opening Supaboard.
Answers come back the way they would in Supaboard: prose, tables, KPIs, and charts rendered as real images in your workspace's theme.
> **Not to be confused with [MCP & Tools](/agents/tools).** That page is about tools Supaboard **calls out to** — your APIs, your services. This page is the opposite direction: Supaboard is the server, and Claude is the client calling it. Same protocol, opposite direction, different audience.
## What You Can Do With It
Once connected, you can ask Claude things like:
* *"What dashboards do we have in the analytics workspace?"*
* *"What's on the Revenue Overview dashboard?"*
* *"How did signups trend last quarter?"*
* *"Switch to the EU workspace and ask the same question."*
Claude works out which Supaboard capability it needs and calls it. You don't invoke anything by name.
## Connecting
Supaboard uses OAuth, so you never paste a token or an API key.
1. Add Supaboard as a connector in Claude, using your Supaboard host as the MCP server URL.
2. Claude opens Supaboard's sign-in page. Sign in with your normal Supaboard account.
3. Choose what the connector should answer from.
### Choosing a workspace and agent
After sign-in you land on a **Choose a workspace** screen:
> Pick the workspace and agent Claude should answer from. You can switch later.
| Section | Behaviour |
| - | - |
| **Workspace** | Every workspace you're a member of. A workspace with no agents is listed but not selectable, marked **No agents**. |
| **Agent** | The agents in the selected workspace. Your default agent is pre-selected. |
A workspace with exactly one agent needs no interaction — it is chosen for you.
Agents that are still indexing your data are shown but flagged as not ready. You can select one, but answers will be poor until indexing finishes.
If you have no workspaces at all you'll see: *"You don't have any workspaces yet. Create one in Supaboard first, then connect again."*
## What Claude Can and Cannot Do
Seven capabilities are exposed:
| Capability | What it does |
| - | - |
| **List namespaces** | Lists the workspaces you can access |
| **List agents** | Lists the agents inside a workspace |
| **Get active workspace and agent** | Reports which workspace and agent questions currently run against |
| **Switch workspace or agent** | Changes which workspace and agent subsequent questions use |
| **List dashboards** | Lists the dashboards in a workspace, grouped |
| **Describe a dashboard** | Describes one dashboard — its widgets, their types, the questions they answer, and the queries behind them |
| **Ask SupaBoard AI** | Runs a full question through Supaboard's AI pipeline and returns the answer |
### Read-only, with one exception
The connector is **read-only against your data**. It cannot create, edit, or delete a dashboard, a query, an agent, or anything else in your workspace.
The one thing it can change is **which workspace and agent it is pointed at**. Switching is non-destructive, persists across the conversation, and does not require reconnecting — ask Claude to *"switch to the marketing workspace"* and it will.
### Access follows your Supaboard permissions
The connector acts as **you**. It can only see workspaces you belong to, agents you're allowed to use, and data your [resource access](/workspace/overview#resource-access) permits. Connecting it does not widen anyone's access, including your own.
## What Answers Look Like
| Result type | How it arrives in Claude |
| - | - |
| **Prose** | Markdown |
| **Tables** | Markdown tables, rendered inline |
| **KPIs** | Rendered inline as text |
| **Charts** | Rendered as images by the same chart engine the Supaboard web app uses, so they match what you'd see on your dashboard — including your workspace's theme and accent colour |
If a chart fails to render, the rest of the answer still comes through with a note in place of the image — one broken chart does not lose you the whole answer.
### Describing a dashboard returns structure, not figures
**Describe a dashboard** tells Claude what a dashboard contains — widget names, types, and the queries behind them. The stored widget values it reports can be hours stale.
For current numbers, ask a question instead of asking about the dashboard. Claude generally does this on its own, but if a figure looks off, asking *"what's the actual current value?"* forces a live query.
## Timing
**Ask SupaBoard AI** runs Supaboard's full pipeline — retrieval, query generation, execution, and summarisation. It typically takes **40 to 120 seconds**.
That is normal, not a hang. Claude waits for it rather than retrying, so let it finish rather than re-asking the same question.
## Other Chat Surfaces
The connector is built on MCP, and the same foundation serves other clients. **Slack** is available today — see [Connect Supaboard to Slack](/analyst/integrations/connect-supaboard-to-slack); it receives its own native block format. **ChatGPT** is in progress on the same foundation and receives an interactive HTML widget rather than images.
## Troubleshooting
| Symptom | Likely cause |
| - | - |
| A workspace appears greyed out with **No agents** | That workspace has no agent yet. Create one in Supaboard first — see [Agents](/agents/overview). |
| Answers are vague or say the data isn't available | The selected agent may still be indexing, or its data scope may not cover what you asked. Check the agent's [data source access](/agents/overview#data-source-access). |
| A question takes over a minute | Expected — see [Timing](#timing). |
| Claude answers from the wrong workspace | Ask it to switch, or check the current one by asking which workspace it's using. |
| You can't see a dashboard you know exists | The connector inherits your permissions. Confirm you can open that dashboard in Supaboard itself. |
# Connect Supaboard to Slack
Source: https://docs.supaboard.ai/analyst/integrations/connect-supaboard-to-slack
Ask your Supaboard agents from Slack. Mention **@Supaboard** in a channel, or message it directly, and the answer — chart, table or number — lands back in the same thread.
Slack is one of three chat surfaces on the same read-only tool layer, alongside [Claude](/analyst/integrations/connect-supaboard-to-claude) and ChatGPT. Nothing asked from a chat can change your data.
## Two steps, two different grants
Connecting Slack involves two separate permissions, and it helps to know which is which.
| Step | Who does it | What it grants |
| - | - | - |
| **Install** the Supaboard app to your Slack workspace | A Slack workspace admin, once | Permission for the bot to read mentions and post replies. It carries **no** access to anyone's Supaboard data. |
| **Link** your Supaboard account | Each person who wants to ask | Your own Supaboard workspace and agent, used for questions you ask. |
An installed workspace where nobody has linked yet answers every question with an invitation to link. That is expected.
### 1. Install the app
Install Supaboard to your Slack workspace from the install link your Supaboard account manager sends you. It opens Slack's own authorisation screen; approve it as a workspace admin.
### 2. Link your account
The first time you mention **@Supaboard**, or if you run `/supaboard link`, the bot replies with a link button. It opens a Supaboard sign-in page where you pick the workspace and the agent that should answer you. You only do this once; the choice is remembered.
## Asking questions
* In a channel: `@Supaboard what was net revenue last week by region?`
* In a direct message: just type the question.
Slack requires an acknowledgement within three seconds, and an analysis takes longer than that, so the bot posts a placeholder immediately and edits the real answer into the thread when it is ready — usually within a minute or two. Charts arrive as images, tables and KPIs as native Slack blocks.
## Slash commands
Everything after `/supaboard` is a command. Without one, it shows help.
| Command | What it does |
| - | - |
| `/supaboard status` | Which Supaboard workspace and agent you are linked to |
| `/supaboard link` | Start, or restart, the account link |
| `/supaboard logout` | Remove your link |
| `/supaboard workspaces` | List the Supaboard workspaces you can reach |
| `/supaboard use-workspace ` | Switch the workspace that answers you |
| `/supaboard agents` | List agents in the current workspace |
| `/supaboard use-agent ` | Switch the agent that answers you |
| `/supaboard dashboards` | List dashboards in the current workspace |
## Limits
* Read-only. The Slack surface can list workspaces, agents and dashboards and ask questions; it cannot create dashboards, edit agents or write to any source.
* One linked account per Slack user. Switching workspace or agent is a command, not a re-link.
* Answers use the same credits as a question asked in the app.
# Write Effective AI Prompts
Source: https://docs.supaboard.ai/analyst/interacting-with-ai
Most bad AI answers trace back to a prompt that left something open: no timeframe, no definition of the metric, or three questions bundled into one. The agent fills those gaps with guesses, and its guess may not match what you meant.
The fix is mechanical. A prompt that works pins down:
| Component | Purpose |
| - | - |
| Timeframe | Defines the analysis period |
| Metric | Specifies exactly what should be calculated |
| Totals | Requests the supporting numbers that let you check the answer |
| Context | Explains the business intent |
| Segmentation | Defines grouping, if you need any |
Say *when*, say *what to calculate*, and ask for *the totals behind the result*. That covers most of it. Things to avoid are the mirror image: no timeframe, several KPIs in one prompt, undefined metrics, subjective wording ("how well", "why is it high").
## Before and after
The examples below are drawn from real failure patterns. In each one the vague version isn't wrong, exactly — it's underspecified, and the answer suffers in a predictable way.
### Counting a problem instead of costing it
Show the number of failed transactions this month.
You'll get a count. But if what you actually care about is the money, the count doesn't tell you — and "this month" may be interpreted as calendar month or last 30 days.
For March, calculate the total value of failed transactions. Include both the number of failed transactions and the total value affected.
### Comparisons with no anchor
How did this month perform compared to last month?
"Perform" on what metric? Which months, exactly? Name the metric and the months, and ask for the difference to be explained:
Give me total revenue and total payments for March and April. Compare the two months and explain the percentage difference.
### Rates with an unclear denominator
What percentage of submissions failed?
Failed out of *what*? All submissions ever? First attempts only? Every rate needs its numerator and denominator spelled out, plus a timeframe:
Out of all submissions in the last 90 days, what percentage failed on the first attempt? Give me total submissions and total failed submissions too.
The same applies to resolution rates, conversion rates, and anything else expressed as a percentage. If timing matters, say which date anchors the calculation — *"use the issue creation date as the reference point"* removes a whole class of ambiguity.
### "Why" questions with no breakdown
Why is the outstanding balance high?
"High" is subjective, and a why-question with no structure invites a subjective answer. Turn it into a measurement with a breakdown, and the "why" tends to fall out of the numbers:
What's the total balance older than 90 days? Break it down by category and by account owner.
Contribution questions work the same way — instead of *"how much revenue came from customers?"*, ask for the amount **and** the percentage of the total it represents, for a named period. Without the reference total, a raw number floats free.
## A template, if you want one
For \[timeframe], calculate \[metric]. Include \[the supporting totals], broken down by \[dimension] if needed.
Fill in the brackets and delete what you don't need. One metric per prompt — if you want five KPIs, five short prompts beat one long one.
# Managing chats
Source: https://docs.supaboard.ai/analyst/managing-chats
Find earlier conversations, keep useful chats close, and share your work.
Chats preserve your questions and the Analyst's answers so you can continue an investigation later.
## Before you begin
You can open chats shared with you or accessible through a project. **Can view** lets you read; **Can chat** lets you ask follow-up questions. The creator manages renaming, moving, sharing, and deletion.
## Find a conversation
1. Open **All chats** in the sidebar.
2. Search for words from the chat title, your questions, or the answers.
3. Use ownership and project filters to narrow the results, then open the chat.
Use **Pinned** for pinned conversations. Chats assigned to a project appear in its **Chats** tab; select the project filter when looking for them in All chats. The default All chats list leaves out project chats and pinned chats to avoid repeating those lists.
## Start a new chat
Click **New** in the sidebar. Inside a project, the new conversation is associated with that project. A separate chat is useful when you move to a different task or audience.
## Pin, favorite, or organize
Open the chat's **⋯** menu:
* **Pin** keeps it in Pinned; **Unpin** removes it from that list.
* Use the favorite action to mark a useful conversation, then filter for favorites in the library.
* Use the project action to file a chat with related work. Destinations depend on your permissions.
Pinning or favoriting helps you find work again. Check a project's members before adding a chat, because project access can affect who can open it.
## Rename or share
A chat's creator can use **Rename** or **Share** in its **⋯** menu. Sharing is also available from the chat's top bar. Choose **Can view** for reading or **Can chat** for follow-ups. Review the people and link access in the dialog before distributing a link.
If you can read a shared chat but cannot rename it or change sharing, ask its creator. Reading access does not make you the owner.
## Delete a chat
The creator can choose **Delete** and confirm. Read the confirmation carefully: deleting a conversation removes its messages. Use **Unpin** or remove it from a project if you only want to change where it appears.
## Next steps
Continue the conversation, or [create a project](/projects/overview) to keep related work together. Find generated dashboards, sites, automations, and files in [Artifacts](/artifacts/overview).
# Ask the Analyst
Source: https://docs.supaboard.ai/analyst/overview
Ask questions in plain English, investigate your data, and create useful outputs.
The Analyst is Supaboard's chat experience. An agent answers questions using the data and business context it can access. Answers can include explanations, charts, tables, and files, and you can ask it to build or edit artifacts.
## Before you begin
You need workspace and agent access. To ask about live business data, that agent needs the relevant [data sources](/data-sources/overview). Your permissions determine which actions are available.
## Open the Analyst
Start on **Home**, or click **New** in the sidebar. If you start from a project, the new chat belongs to that project.
### The empty state
The message box says **Build Anything...**. Ask a question directly or use a **Dashboard**, **Datasite**, **Automation**, **Pdf**, or **Deck** shortcut. These fill in a starting prompt for you to complete; they do not send it immediately.
## Asking a Question
Choose an agent, type your question, and send it. Include the period, metric, and comparison you need: “Compare completed orders this month with last month and explain the biggest changes by region.”
### Choosing an agent
Open the agent picker in the message box. Select the agent whose data scope and knowledge fit the question. You can change agents during a conversation; your selected agent and mode are remembered for the session.
### Query modes
| Mode | Use it for |
| - | - |
| **Flash** | Everyday questions and quick follow-ups |
| **Pro** | Deeper analysis of your data |
| **Max** | Complex tasks that benefit from planning before investigation |
Older guides use **Concise** and **Deep Dive** for the first two modes. The current picker shows **Flash**, **Pro**, and **Max**. Tell the Analyst if you want a short answer or a detailed explanation.
### Voice input
Use the microphone in the message box to dictate instead of typing. Review the text before sending.
### Attachments and tools
Use the **+** menu to add supporting material. Context helps the Analyst understand the files or existing work your question refers to.
#### Files and artifacts
Attach a document, image, or spreadsheet, or select an existing artifact as context. Review the context chips before you send. Follow-up questions can build on the conversation and its attached context.
#### Connected tools
Select connected MCP tools when the task needs information or actions from another service. An administrator can [configure tools](/agents/tools). The connected service's permissions still apply.
## Understanding Responses
The response streams into the chat. Its activity display shows progress, such as work being performed and tools being used. The answer contains the explanation and any supporting visuals or artifacts.
### What an answer can contain
| Result | What you can do with it |
| - | - |
| Explanation | Read conclusions, assumptions, and suggested follow-ups |
| Chart, table, or KPI | Inspect the data and open or edit the visual |
| Artifact card | Open a dashboard, Datasite, automation, or file in a side panel |
| Generated file | Preview and download the output |
### Clarifying questions
The Analyst may ask you to clarify a date range, a definition, or what to build. Answer to continue. When an action requires approval, review the requested action before allowing it.
### The message toolbox
Use a message's controls to copy or share it. A widget has controls including **Open**, **Edit**, and **Pin**, plus a **⋯** menu. Available actions depend on the result and your access.
### Rating an answer
Use **Like** or **Dislike** in a widget's **⋯** menu. If the result is wrong, explain what needs correcting when prompted. Feedback helps improve future answers.
### Platform help
You can also ask how to use Supaboard—for example, “How do I schedule a dashboard report?” See [Understand answers](/analyst/insights-in-ask-agents) for ways to inspect and refine results.
## Pinning Widgets
Choose **Pin** on a useful chart, table, or KPI and select an available destination, such as a [dashboard](/dashboards/overview) or [Datasite](/datasites/overview). You need permission to add content to the destination.
To build a whole output instead, ask “Turn this analysis into a dashboard.” Open the artifact card, inspect it, and request changes in the same conversation.
## Return to your work
Find recent chats in the sidebar, open **All chats**, or use **Pinned** for pinned conversations. Project chats also appear inside their project. Click **New** to start a separate conversation.
For searching, renaming, organizing, and sharing conversations, see [Managing chats](/analyst/managing-chats). A chat's creator controls sharing, renaming, and deletion.
## Permissions
Your [workspace access](/workspace/overview#feature-access), access to the selected agent, and the chat's sharing settings determine what you can read or change. Reading a shared chat does not necessarily let you send messages, edit an artifact, or manage sharing.
## Quick Reference
| I want to… | Where to start |
| - | - |
| Ask a new question | Home or **New** |
| Improve my question | [Write better prompts](/analyst/interacting-with-ai) |
| Understand an answer | [Understand answers](/analyst/insights-in-ask-agents) |
| Find an earlier chat | [Managing chats](/analyst/managing-chats) |
| Keep work together | [Projects](/projects/overview) |
| Create a reusable output | [Artifacts](/artifacts/overview) |
# Artifacts
Source: https://docs.supaboard.ai/artifacts/overview
Find, create, and work with dashboards, Datasites, automations, and generated files.
An artifact is an output you can use beyond an individual answer. **Artifacts** is the library for these outputs in your workspace.
## Before you begin
You see artifacts available through ownership, direct sharing, general workspace access, or a project. Creating or changing an artifact also depends on your workspace role and feature access.
## Find an artifact
Open **Artifacts** in the sidebar. Choose **All**, **Datasites**, **Dashboards**, **Automations**, or **Files**. Search by name and use project, ownership, or favorite filters to narrow the list.
Switch between **Grid** and **Table** views. The library shows type, owner, project, and last update. Datasites and automations also have statuses, such as a draft site or paused automation.
## Choose what to create
Track metrics with charts, tables, KPIs, and filters.
Create an interactive site around your data and publish it when ready.
Run a data workflow on a schedule or when an alert condition is met.
Turn analysis into files for a review, meeting, or handoff.
## Create an artifact
You need permission to create the chosen output and access to its data.
1. Select **New Artifact**.
2. Choose **Dashboard**, **Datasite**, **Automation**, **PDF**, or **Deck**.
3. Enter a name and describe the result you want. Optionally choose a project.
4. Select **Build with AI** to open a new Analyst chat with your brief ready to use.
Dashboards, Datasites, and automations also offer **Build Manually** to open their editor. PDFs and decks are built with AI.
You can start from an existing Analyst conversation too: ask it to create an output from the work you are discussing.
## Open and edit
Open an artifact from the library or its card in a chat. A chat card opens a preview or editor beside the conversation so you can inspect the output while requesting changes.
For editable artifacts, the **⋯** menu includes actions such as **Edit in chat**, **Edit manually**, and assigning a project. Generated files use file-specific actions. Actions depend on the type and your access.
## Share the result
Each output has its own sharing or delivery flow. Share a dashboard, publish a Datasite, schedule or activate an automation, or download a file using that feature's controls. Creating an artifact alone does not publish a site or turn on an automation.
## Next steps
Your artifact is ready when it appears in the library and opens with the expected content. Use the feature guides above to finish and share it. Keep it with its supporting analysis by [organizing it in a project](/projects/overview).
# Reports and presentations
Source: https://docs.supaboard.ai/artifacts/reports-and-presentations
Turn an analysis into a PDF report or presentation you can review and share.
Ask the Analyst to prepare a report or presentation from your data or an existing analysis. Describe the audience, time period, and decisions the output should support.
## Before you begin
Choose an agent with access to the relevant data. If the output should use an existing dashboard, analysis, or reference document, add it as context.
## Create a report or deck
1. Start a chat, or continue the conversation containing the analysis.
2. Ask for the output you need. For example: “Create a presentation for our monthly business review. Compare last month with the previous month, explain the three biggest changes, and end with proposed actions.”
3. Answer any clarifying questions and review the generated file card.
You can also use **Artifacts → New Artifact → PDF** or **Deck**. Add a name, description, and optional project, then choose **Build with AI**. These file types have no manual creation option in that dialog.
## Review and revise
Open the output from its chat card. Check figures, dates, definitions, and presentation before sharing.
Request changes in the same chat: “Use our uploaded deck as a style reference,” “Add a slide on retention,” or “Shorten this to a one-page summary.” Keep the relevant file or artifact in context so the Analyst knows which output you mean.
## Find and download the file
Generated documents appear in **Artifacts → Files**. They also appear in the project's **Artifacts** tab when associated with a project. Open the file and use its download control to save it.
## Export or schedule a dashboard report
For a one-off output, follow [Export a dashboard report](/dashboards/how-to/export-dashboard-reports). For repeated delivery, use [Schedule dashboard reports](/automations/schedule-dashboard-reports).
## Access
**Can view** allows opening, downloading, and adding a file as chat context. **Can edit** also allows revising it with AI and exporting it again, subject to workspace editing permission. Only the owner manages sharing.
## Next steps
Your report is ready to share once you have reviewed its content and downloaded the intended version. Keep the analysis and output in a [project](/projects/overview), or return to the chat for the next revision.
# Create and test an automation
Source: https://docs.supaboard.ai/automations/create-an-automation
Build a scheduled workflow, check each step, and activate it when ready.
Use an automation for work that should repeat: check stock, prepare a report, or notify a team when a metric needs attention.
## Before you begin
You need Automations feature access and permission to create a workflow. Have the data source, timing, recipients, and desired output ready. To add steps to an existing shared automation, you need **Can create** access.
## 1. Create a draft
Open **Artifacts → New Artifact → Automation**. Name the workflow and choose its project. Add a description and choose **Build with AI**, or choose **Build Manually** to use the flow editor.
A useful brief includes the check, timing, timezone, and output:
> Every Monday at 9am in Asia/Kolkata, check stock for active products. Prepare a table of products below their reorder level and email it to the operations team. Include a “no shortages” message when none are below the threshold. Show me the workflow before activating it.
## 2. Set the trigger
In the manual editor, choose **Add Trigger / First Step**. Use **Timed Start** for a schedule, or **Alerts** for a query-based condition. Select the timezone as well as the time for a clock-based schedule.
For an alert, the query must return a boolean column named `run_alert`. See [Alert queries](/automations/overview#alerts) for the exact result format.
## 3. Add and configure steps
Add the tasks the workflow needs, such as **Query Your Data**, a document step, and **Send Email**. Open each step's **Setup** tab and fill its required fields. Give steps descriptive titles so they are easy to recognize later.
Use the variable picker to pass a query result into the next step. Confirm that email addresses, date ranges, and selected data sources are correct.
## 4. Test deliberately
Open a query step's **Test** tab and run it to inspect its output. Once the individual steps are correct, use **Test Run** to check the complete flow.
Tests execute their steps. Testing an email or webhook step can send the message or request. Use intended test recipients and review the actions before running the full flow.
## 5. Publish and monitor
Choose **Publish** when the trigger and output are correct. A draft does not run automatically. Open **View Logs** on the published automation to inspect scheduled runs and each step's result.
To change a published workflow, use **Edit Automation**, make and check the changes, and publish again. Editing a published workflow stops its scheduled execution until it is republished.
## Expected result
The published automation runs when its trigger is due, and its run history shows the outcome. Check the first scheduled run to confirm the destination and output.
## Next steps
For a dashboard email, use [Schedule dashboard reports](/automations/schedule-dashboard-reports). See the [automation reference](/automations/overview) for actions, variables, and conditions.
# Automations
Source: https://docs.supaboard.ai/automations/overview
Schedule data workflows, alerts, and recurring reports.
**Automations** run your data work on a schedule or when your data changes — without anyone opening Supaboard. An automation is one **trigger** plus a canvas of **steps** that pass data to each other: query a table, ask an AI analyst about it, branch on the result, build a PDF, and email it to your team.
Where a [dashboard](/dashboards/overview) is something you *look at* and [Query Bench](/query-bench/overview) is something you *ask once*, an automation is something that keeps happening.
If you cannot create an automation, ask your administrator to check the workspace plan and your feature access.
## Before you begin
You need Automations feature access and permission to create the workflow. Have the timing, data source, recipients, and desired output ready.
Build, test, and activate a recurring workflow.
Send a dashboard report on a schedule.
## The Automations Home
Open **Artifacts → Automations** to find workflows in your workspace. Search by name, filter by project or ownership, and switch between **Grid** and **Table** views. The library shows an automation's status, such as draft, active, paused, or failing.
### Creating an automation
Select **New Artifact → Automation**, give it a name, and optionally assign a project. Choose **Build Manually** to open the flow editor, or add a description and choose **Build with AI** to prepare the workflow through the Analyst.
You can also ask the Analyst to create an automation from a chat. Review the trigger and steps, test them, and publish the automation when it is ready. Creating a draft does not activate it.
## Triggers
Every automation has exactly one trigger, and it must be set before you can add any steps. There are two trigger types.
> There is no webhook, event, or cron-expression trigger. Automations start either on a clock or on a query result.
### Timed Start
> *"Run automation on a time-based schedule."*
Pick a **regularity**, and the fields you need appear beneath it.
| Regularity | Additional fields | Behaviour |
| - | - | - |
| **interval** | **interval** (seconds) | Runs on a rolling interval |
| **daily** | **time\_of\_day** | Runs once a day at that time |
| **weekly** | **day\_of\_week**, **time\_of\_day** | Runs once a week on that day |
| **monthly** | **day\_of\_month** (1–31), **time\_of\_day** | Runs once a month on that date |
| **yearly** | **month\_of\_year** (1–12), **time\_of\_day** | Runs once a year in that month |
`time_of_day` is entered as `HH:MM` on a 24-hour clock.
> **Choose the schedule’s timezone.** For daily, weekly, monthly, and yearly schedules, enter the local clock time and select the **timezone** (for example, `Asia/Kolkata`). Do not convert the time to UTC yourself. An empty timezone uses UTC; interval schedules use elapsed seconds.
> **Very short intervals are not real.** The scheduler checks triggers roughly every five minutes and enforces a 60-second minimum on any interval, so an automation set to run every 30 seconds will not run every 30 seconds. Treat five minutes as the practical floor.
### Alerts
> *"Run automation when alert query conditions are met."*
Instead of a clock, this trigger runs a query against your data on every scheduler cycle and fires when the query says so.
| Field | Required | Notes |
| - | - | - |
| **resource** | Yes | The data source to query |
| **database** | Yes | Populated once a resource is selected |
| **table** | Yes | Populated once a resource and database are selected |
| **query** | Yes | The alert condition, written as SQL |
> **Your query must return a boolean column named `run_alert`.** The automation fires when the first row's `run_alert` value is true. A query that doesn't produce that exact column will never fire, and nothing in the interface will tell you why.
A working alert query looks like this:
```sql theme={null}
SELECT
COUNT(*) > 100 AS run_alert,
COUNT(*) AS failed_orders
FROM orders
WHERE status = 'failed'
AND created_at > NOW() - INTERVAL '1 hour'
```
`run_alert` decides *whether* to fire. Every other column in that first row is passed to your downstream steps as data, so include the numbers you want to put in the alert email — here, `failed_orders`.
> **Alerts fire on change, not on every check.** The trigger fingerprints your query together with its first row of results. If that row is identical to the last time it fired, it stays quiet. So a persistently broken condition alerts you once, not every five minutes — but it also means a value that returns to the same number after changing will alert again.
If any of the four fields is left blank, the trigger silently never fires. Fill in all four.
## Building the Flow
The editor is an infinite canvas. Your trigger sits at the top and steps flow downwards.
1. **Add the trigger.** A blank canvas shows a single dashed node labelled **Add Trigger / First Step**. Click the **+** to open **Set Trigger** and pick one of the two triggers above.
2. **Add steps.** Once a trigger exists, the node below reads **Add Task**. Clicking it opens the **Add Action** panel, with **Actions** and **Flow Control** in the left rail and a search box for finding an action by name.
3. **Configure each step.** Clicking a node opens its panel on the right, which has two tabs:
* **Setup** — the node's **Title** (edit this; it's how the step is labelled on the canvas and in the variable picker) and all of its configuration fields. Required fields are marked with `*`.
* **Test** — run this step on its own and inspect what it returns.
The **Change** link at the top of the Setup tab swaps a node's type in place, keeping its position on the canvas.
4. **Connect them.** Steps run in the order you wire them. Hovering a node reveals **Delete** and **Change type** buttons.
Your work saves automatically. The topbar shows **Saving…** and **Unsaved changes** as you go.
## Actions
### Communication
#### Send Email
> *"Send reports, files, or notifications to one or more recipients."*
| Field | Required | Notes |
| - | - | - |
| **to** | Yes | One address, a comma-separated list, or a variable (see below) |
| **subject** | Yes | Single line; can contain variables |
| **body** | Yes | Rich text, with a **Text** / **Code** toggle |
The **to** field is deliberately free text, so you can bind it to data. All of these work:
* `ops@yourcompany.com`
* `ops@yourcompany.com, finance@yourcompany.com`
* `{{ query.0.EMAIL }}` — one address from the first row of an upstream query
* `{{ query.*.EMAIL }}` — every address in that column, one email sent to all of them
Duplicate addresses are removed automatically.
**Body modes:**
| Mode | Behaviour |
| - | - |
| **Text** | Your text is wrapped in a branded email template carrying your **workspace name and logo**. Line breaks are preserved. This is the default. |
| **Code** | Your HTML is sent exactly as written, with no wrapper. Opens a full-screen editor with a live preview beside it. Use inline CSS for mail-client safety. |
### Integration
#### Send Webhook
> *"Send workflow data to another app using an HTTP webhook."*
| Field | Required | Default | Notes |
| - | - | - | - |
| **webhook\_url** | Yes | — | The endpoint to call |
| **payload** | No | `{}` | The JSON body; bind variables into it |
| **headers** | No | — | Extra request headers |
| **timeout\_seconds** | No | `30` | How long to wait for a response |
| **include\_timestamp** | No | On | Adds a `timestamp` field to the payload |
Requests are always sent as `POST` with `Content-Type: application/json`. The response status code and body are available to downstream steps, so you can branch on whether the call succeeded.
### Data
#### Query Your Data
> *"Run a SQL/database query on a selected resource"*
| Field | Required | Notes |
| - | - | - |
| **resource** | Yes | The data source |
| **database** | Yes | Populated from the selected resource |
| **table** | Yes | Populated from the resource and database |
| **query** | Yes | SQL or Python, written in a full code editor |
This is the workhorse step — most automations start by pulling a result set here and then act on it. Every column it returns becomes available to later steps through the variable picker.
### Document
Six actions generate a formatted document. Three start from a dashboard, three start from raw data you pass in.
**From a dashboard:**
| Action | Output |
| - | - |
| **Export Dashboard as PDF** | A formatted PDF report |
| **Export Dashboard as PowerPoint** | A slide deck |
| **Export Dashboard to Excel** | An Excel workbook |
| Field | Required | Notes |
| - | - | - |
| **dashboard** | Yes | Which dashboard to export |
| **filters** | No | Apply dashboard filters before exporting |
| **prompt** | Yes | Instructions for how the document should be written |
| **title** | Yes | The document's title |
**From data:**
| Action | Output |
| - | - |
| **Generate PDF from Data** | A PDF built from records you pass |
| **Generate PowerPoint from Data** | Slides built from records |
| **Generate Excel** | A workbook built from records |
These take a **data** field instead of a dashboard — bind it to an upstream step, for example `{{ query.* }}` — plus the same required **prompt** and **title**.
> **The prompt does real work.** These documents are written by AI, and the prompt is how you steer it. *"A one-page executive summary: headline revenue number, top three regions, and any metric that moved more than 10% week over week"* produces a very different document from *"Summarise this"*. Describe structure, tone, and what matters.
Each of these steps outputs a **url** you can drop into a **Send Email** step.
### AI
| Action | What it does | Key fields |
| - | - | - |
| **Answer Questions with AI** | Answers a question against data you supply | **prompt**, **data** |
| **Summarize Insights** | Writes a concise analytical summary of a result set | **data**, **user\_query** (optional focus) |
| **Ask Dashboard** | Asks a question of a dashboard's live data | **dashboard**, **filters**, **prompt** |
| **Ask Analysts** | Runs a full [the Analyst](/analyst/overview) turn with one of your [agents](/agents/overview) | **agent**, **search**, **conversation** |
| **Evaluate AI Analyst** | Scores an agent's answer quality and returns issues and recommendations | **agent** |
| **Generate Structured Output** | Returns AI output in a fixed shape so later steps can read specific fields | **prompt**, **data**, **output\_format** |
**Generate Structured Output** is the one to reach for when a later step needs a *specific value* rather than a paragraph. Its **output\_format** field opens a visual builder where you define each field, its type, and its allowed values — and the AI is then required to answer in that shape. Bind a downstream **Condition** to one of those fields to branch on what the AI decided.
**Ask Analysts** has a **fast** toggle, on by default. Turn it off for deeper, slower analysis.
## Passing Data Between Steps
Any step can read the output of any step that ran before it. This is what turns a list of actions into a workflow.
### The variable picker
Type `/` in any text field to open **Insert variable**. It lists your upstream steps and their columns. Each step has a **Run node** button — click it to execute that step now and see its real columns and sample values, so you're picking from actual data rather than guessing at names.
Pick a value and it's inserted as a chip. Underneath, that chip is a template.
### The template syntax
| Pattern | Resolves to |
| - | - |
| `{{ step.0.COLUMN }}` | One cell — column `COLUMN` from the first row |
| `{{ step.0 }}` | The entire first row |
| `{{ step.* }}` | Every row |
| `{{ step.*.COLUMN }}` | One column, from every row |
| `{{ a.*.X & b.*.Y }}` | Two references combined |
The `&` operator is smart about how it combines: when the two sides have **different** columns they merge side by side into wider rows; when they have the **same** columns they stack into a longer list. The two sides can come from different steps.
### Two forms that won't work
Both of these are rejected when you save, with an error explaining the fix:
| Don't write | Write instead | Why |
| - | - | - |
| `{{ step[0].COLUMN }}` | `{{ step.0.COLUMN }}` | Paths are dot-separated — a row index is `.0`, not `[0]` |
| `{{ step.data.COLUMN }}` | `{{ step.COLUMN }}` | A step's data is implicit; reference its columns directly |
## Conditions and Flow Control
### The Condition step
> *"Branch the flow: each output runs only when its condition is met."*
Found under **Flow Control** in the **Add Action** panel. A Condition has a **mode** and a set of labelled **outputs**, each with its own rules.
| Mode | Behaviour |
| - | - |
| **All matching** | Every output whose rules pass fires |
| **First matching** | Only the first output whose rules pass fires |
Each rule reads left to right: a **value**, a **type**, an **operator**, and a comparison **value**. Both value fields accept a literal or a variable, so a typical rule is `{{ query.0.FAILED_COUNT }}` · number · `gt` · `100`.
**Types:** `string`, `number`, `bool`, `date`.
**Operators**, filtered by the type you choose:
| Group | Operators |
| - | - |
| Comparison | `gt`, `gte`, `lt`, `lte`, `eq`, `neq` |
| Text | `contains`, `not_contains`, `starts_with`, `ends_with` |
| Presence | `is_empty`, `is_not_empty` |
| Set | `in`, `not_in` |
| Boolean | `is_true`, `is_false` |
The presence and boolean operators don't need a second value, so the right-hand field disappears when you pick one.
Add more than one rule to an output and a combinator appears: **Match ALL rules** or **Match ANY rule**.
One output can be marked as a **Fallback** — it fires when nothing above it matched, and it carries no rules of its own. You can have at most one.
> **An output with no rules always fires.** If you leave an output's rule list empty, it's treated as passing. Either give it rules or make it the Fallback.
> **Parallel steps are not conditions.** If you hang two emails off a single step, *both* emails send on *every* run — the connections are order, not choice. Whenever you mean "either this or that", put a Condition between them.
### Paths and Merge
Two more Flow Control blocks:
* **Paths** — *"Build different steps for different rules"*. Splits the flow into parallel branches that all run.
* **Merge** — *"Merge data from multiple streams into one"*. Brings parallel branches back together so a later step can read all of them.
## Testing
### Testing one step
Open a node and switch to its **Test** tab, then click **Run**. The step executes on its own and shows you what it returned. Validation problems appear as a numbered list.
This is how you check a query returns what you expect before wiring anything to it.
### Testing the whole automation
The **Test run** button (play icon) in the toolbar opens the **Test Run** panel. Click **Run** and watch a live timeline as each step executes, with per-step durations and expandable results. **Cancel** stops it; **Run Again** repeats it.
> **A Test Run is a real run.** Emails are actually sent, webhooks actually fire, and documents are actually generated. There is no dry-run mode for the full flow. When you only need to check that a query or an AI step behaves, use the per-step **Run** in the Test tab instead.
> **Test Runs don't appear in Run History.** They're for iterating, not for auditing. Only scheduled runs are recorded.
## Publishing
A new automation is a draft and will not run on its own, no matter what its trigger says. Click **Publish** to make it live. The first scheduled run is set for about a minute later.
> **Publishing locks the canvas.** A published automation can't be edited — nodes won't move, and the add and delete affordances disappear. The **Publish** button becomes **Edit Automation**; clicking it unpublishes the automation so you can make changes, which also stops it running until you publish again.
## Run History
The **View Logs** button on a published automation opens the **Run History** panel — every scheduled run, newest first, 50 at a time. Expand any entry to see each step's result, the event log, and how long the run took.
| Status | Meaning |
| - | - |
| **Completed** | Every step succeeded |
| **Completed with errors** | The run finished, but at least one step failed |
| **Failed** | The run did not complete |
| **Running** | Currently executing |
| **Skipped due to condition** | A Condition routed around this step. This is normal, not an error. |
| **Skipped due to dependency failure** | A step this one depended on failed, so it never ran |
> **There are no automatic retries.** A step that fails stays failed, and every step downstream of it is skipped. If a step calls something that's occasionally flaky, build the retry into the flow yourself — a Condition on the previous step's `success` value can route to a second attempt.
If a trigger itself errors — an alert query against a table that no longer exists, for instance — the automation is marked with an error, which clears on the next successful check.
## Building With AI
### The AI Analyst
Choose **Edit with AI** in the toolbar to open the automation beside the Analyst chat. You can also begin with **Build with AI** from New Artifact.
> **Build automations with AI**
> Describe what you want to automate — the analyst can create and edit steps, rewire them, test them, and publish for you.
Describe what you want in plain English and it builds it: creating steps, filling in their fields, connecting them, running them to check, and publishing when you're happy.
Choose an agent with the relevant data access. Explain the intended trigger, steps, destinations, and any changes you want. Review the resulting workflow and any requested approvals before testing or publishing.
### Generating a field's contents
Two fields have a dedicated AI writer.
**Write Code** — the full-screen editor behind any query field. Choose **Python** or **SQL**, describe what you want, and click **Generate with AI**. The suggestion appears as a diff over your existing code — **accept** or **reject** it. **Save & Run Query** executes it against the node's selected resource and shows the results below, so you can confirm it works before closing.
**Edit HTML email body** — the same flow for a **Send Email** body in Code mode, with a live preview beside the editor.
> The HTML writer will never invent variable bindings. Where your data should go, it leaves a placeholder like `[[ total revenue this month ]]` in double square brackets. Replace each one by clicking it and typing `/` to pick the real value.
## Scheduling a Dashboard Report
Open the dashboard's **More options → Email Reports**. Choose the schedule, timezone, filters, output format, and recipients, then review before choosing **Set Schedule**. This activates scheduled delivery.
See [Schedule dashboard reports](/automations/schedule-dashboard-reports) for a walkthrough with the current form.
## Permissions
Workspace feature access and the automation's sharing settings both apply.
| Access | What it allows |
| - | - |
| **Can view** | Inspect steps, settings, and run history |
| **Can edit** | Also change existing steps, run or test them, turn the workflow on or off, and rename it |
| **Can create** | Also add new steps |
| **Owner** | Also share or delete the automation |
Changes require an editing role in the workspace. Access may be inherited from a project.
## Next steps
[Build your first workflow](/automations/create-an-automation) or [schedule a dashboard report](/automations/schedule-dashboard-reports).
# Schedule a dashboard report
Source: https://docs.supaboard.ai/automations/schedule-dashboard-reports
Send a recurring dashboard report with the right time, data slice, format, and recipients.
Use a scheduled report for a regular review, such as a Monday sales summary. Supaboard creates an automation that prepares and sends the report at the frequency you choose.
## Before you begin
Open the dashboard page you want to report on. You need access to the reporting action and permission to create the resulting automation. Decide who should receive the data and which period the report should cover.
## Configure the delivery
1. Open **More options → Email Reports** on the dashboard.
2. Enter a recognizable **Report Name**.
3. Choose **Hourly**, **Daily**, **Weekly**, or **Monthly** in **Delivery Frequency**.
4. Set the day or minute, time, and timezone shown for that frequency.
5. Set **Filters** for the report. A relative period is useful when each run should cover the latest week or month.
6. Choose **Text mail**, **PDF**, **PPT**, or **Excel**.
7. Add recipient email addresses in **Delivery Destination**. Review the list if you use **Add dashboard members**.
8. Add any **Custom Instruction for Report**, such as the key changes to explain.
A useful instruction is: “Summarize the previous week's performance, compare it with the week before, and highlight the three largest changes. Include the period covered.” The filter configuration and the writing brief should describe the same period.
## Activate the schedule
Review the settings, then select **Set Schedule**. This creates and publishes the scheduled automation; it is the step that enables recurring delivery.
The confirmation provides an option to open the automation for further editing. You can also find it under **Artifacts → Automations**.
## Check a run or change the schedule
Open the automation to inspect its trigger, report steps, recipients, and run history. Review a completed run to confirm that the output covers the intended period. Pause the automation before making changes you do not want delivered yet.
Testing an automation can run its delivery steps. Use appropriate test recipients before running a flow that sends emails or calls another service.
## Expected result
The schedule is active, the automation appears in your library, and its run history shows the recurring report's outcome. If a run fails, open its details to identify which step needs attention.
## Next steps
Read the [automation guide](/automations/overview) for more complex flows, or [create a one-off report](/dashboards/how-to/export-dashboard-reports).
# Add Filters and Environments in Embedded Dashboard Programatically
Source: https://docs.supaboard.ai/dashboards/embedding/add-filters-and-environments-in-embedded-dashboard-programatically
Supaboard lets you embed any dashboard into your own website or application. Two features give you precise control over what viewers see and which data they query: parameterised filters and environments.
## Parameterised Filters
### What Are Parameterised Filters?
Every filter widget on your dashboard (date pickers, dropdowns, search inputs, etc.) can be individually configured per embed. You decide whether a filter is shown to the viewer, hidden but silently applied, or removed entirely.
This lets you do things like:
* Show a "Region" filter so viewers can explore different markets — but lock the "Date" filter to the current quarter so they can't change it.
* Silently scope an embed to a specific customer's data without exposing that filter in the UI.
* Remove filters that aren't relevant to a particular audience.
### The Three Filter Modes
| Mode | What the viewer sees | What data is queried |
| - | - | - |
| **Visible** | The filter widget, interactive | Whatever the viewer selects (or the preset value if set) |
| **Hidden with preset** | Nothing — the filter is invisible | The preset value you configured, applied automatically |
| **Excluded** | Nothing — the filter is invisible | No filter applied; the full unfiltered dataset is used |
### Configuring Filters in the Embed Dialog
When you create an embed, open the **Filters Visibility and Presets** section:
1. Each filter widget in your dashboard is listed by name.
2. **Check the box** next to a filter to keep it visible. **Uncheck it** to hide or exclude it.
3. Use the **preset value** picker on the right of each filter to pre-fill it with a value.
**How the checkbox and preset interact:**
* **Checked, no preset** — filter is shown; the viewer can freely change it.
* **Checked, with preset** — filter is shown pre-filled with your value; the viewer can still change it.
* **Unchecked, with preset** — filter is hidden; your preset value is applied silently (data is scoped).
* **Unchecked, no preset** — filter is excluded; no filtering is applied on that field.
### Preset Value Types
The preset input adapts to the type of filter:
| Filter type | How to set the preset |
| - | - |
| Text / search | Type the value directly |
| Number | Type a number |
| Boolean (on/off) | Choose On, Off, or Not set |
| Dropdown (categorical) | Select one or more values from the list |
| Single date | Pick a date from the calendar |
| Date range | Pick a start and end date |
| Month | Pick a month |
| Quarter | Pick a quarter |
| Year | Type a year (e.g. 2024) |
| Relative date | Choose a named period: **This month**, **Last 7 days**, **Last 30 days**, etc. |
### Filters Across Multiple Pages
If your dashboard group has multiple pages, the filter panel groups them by page. Select a page to configure its filters, then use the back arrow to switch to another page. Filter settings from all pages are saved together in the embed.
***
## Environments
### What Are Environments?
Environments let a single embedded dashboard point at different databases or data sources — for example, showing production data to your customers while using the same dashboard layout to show staging data to your QA team.
When you switch environments, every widget query is automatically re-routed to the data source mapped to that environment. No changes to the dashboard itself are needed.
### Common Use Cases
* **Multi-tenant data isolation** — route each customer's embed to their own database.
* **Production vs staging** — use the same dashboard for testing with non-production data.
* **Regional data sources** — point to different regional databases per embed.
### Setting the Environment on an Embed
In the **New Embed** dialog, find the **Environment** dropdown. It lists all environments configured for your dashboard group. Select the one you want this embed to use by default.
If no environment is selected, the dashboard group's default environment is used.
### How Environments Are Set Up
Environments are created inside your dashboard group settings. Each environment has a **resource map** — a list of data-source substitutions. When that environment is active, any widget that queries source A will automatically query source B instead, as defined in the map.
Contact your workspace admin to create or modify environments.
***
## Embed Options at a Glance
When creating an embed, you can configure:
| Option | Description |
| - | - |
| **Expiry date** | When the embed link stops working |
| **Allowed websites** | Restrict which domains can display the embed |
| **Filters** | Per-filter visibility and preset values (see above) |
| **Environment** | Which data source the embed queries by default |
| **Hide title** | Remove the dashboard title from the embedded view |
| **Allow export** | Let viewers download data from charts, tables, and KPIs |
| **Theme** | Force light mode, dark mode, or follow the viewer's system setting |
| **Interactive mode** | Enable the Stella AI assistant inside the embed (Pro) |
***
## Setting Filters Programmatically via URL
You can control filter values at runtime — without recreating the embed — by appending a `filter` query parameter to the iframe `src`. This is useful when the viewer's identity or context is known server-side and you want to scope the dashboard automatically (e.g. show each customer only their own data).
### How It Works
The `filter` parameter is a **base64-encoded JSON object** that maps each filter widget's ID to its configuration. When present, it completely replaces any filter settings saved at embed-creation time.
The JSON shape:
```json theme={null}
{
"": {
"hidden": true,
"disabled": false,
"presetValue": ""
}
}
```
* **`hidden`** — `true` hides the filter widget from the viewer; `false` shows it.
* **`disabled`** — `true` removes the filter entirely (no filtering applied); `false` applies it.
* **`presetValue`** — the value to pre-fill or silently apply. See [Preset Value Types](#preset-value-types) for formatting by field type.
### Finding a Widget ID
Each filter widget has a unique ID visible in the dashboard editor. Open the filter widget's settings panel — the ID is shown under **Widget ID** (it looks like `wgt_abc123`).
### Encoding the Filter Config
The JSON must be base64-encoded before being placed in the URL.
**In the browser (JavaScript):**
```javascript theme={null}
const filterConfig = {
"wgt_abc123": {
hidden: true,
disabled: false,
presetValue: "acme-corp"
}
};
const encoded = btoa(JSON.stringify(filterConfig));
const iframeSrc = `${baseEmbedUrl}?filter=${encoded}`;
```
**In Node.js (server-side rendering):**
```javascript theme={null}
const filterConfig = {
"wgt_abc123": {
hidden: true,
disabled: false,
presetValue: "acme-corp"
}
};
const encoded = Buffer.from(JSON.stringify(filterConfig)).toString("base64");
const iframeSrc = `${baseEmbedUrl}?filter=${encoded}`;
```
### End-to-End Example
Say your dashboard has a "Company" filter (`wgt_abc123`) and a "Date Range" filter (`wgt_def456`). You want to:
* Lock "Company" to the current viewer's company (hidden, silently applied).
* Keep "Date Range" visible but default it to the last 30 days.
```javascript theme={null}
const filterConfig = {
"wgt_abc123": {
hidden: true,
disabled: false,
presetValue: "acme-corp" // viewer's company, set server-side
},
"wgt_def456": {
hidden: false,
disabled: false,
presetValue: "last_30_days" // relative date token
}
};
const encoded = btoa(JSON.stringify(filterConfig));
document.getElementById("dashboard-frame").src = `${baseEmbedUrl}?filter=${encoded}`;
```
### Other Parameters
#### default\_dashboard\_page
Sets which page loads when the embed first opens, overriding the default page configured in the embed token.
Value: The uniquelink (ID) of the dashboard page you want to show.
Example: [https://your-embed-url.com/embed/dashboard/>token\token\token\token\token\token\` to the embed URL to override the default. The environment ID can be found in your dashboard group's environment settings.
**Can a viewer tell that a preset filter is applied?**
Not if the filter is hidden. The filter widget will not appear in the embedded view, and the data will already be scoped to your preset value.
**What happens if I exclude a filter?**
The filter widget is hidden and the filter is not applied at all. Viewers see the full, unfiltered dataset for that field.
**Can I set a relative date like "last 30 days" as a preset?**
Yes. When configuring a date filter's preset, choose from the relative date options: **Today**, **Yesterday**, **This week**, **Last 7 days**, **This month**, **Last 30 days**, **Last 3 months**, **This year**, and more. These always resolve to the current period when the dashboard loads.
**Can different embeds for the same dashboard use different filters and environments?**
Yes. Each embed is independent. You can create multiple embeds of the same dashboard, each with a different filter configuration and environment.
# Dashboard Embeds
Source: https://docs.supaboard.ai/dashboards/embedding/overview
**Dashboard Embeds** let you share any dashboard with an external audience by generating a shareable URL you can drop into an iframe. The embedded dashboard is read-only — viewers can interact with charts and apply filters, but cannot edit anything.
> Embeds are for sharing a *specific dashboard*. If you want to deliver a fully branded, multi-feature analytics experience to your own users under your own domain, see [White Label](/workspace/white-label) instead.
## Opening the Embed Panel
Open a dashboard, click the **…** options button in the top-right toolbar, and select **Embed**.
From here you can create new embed tokens and manage existing ones for this dashboard group.
## Creating an Embed
Click **Add New Embedding** to open the creation form. Configure the options below, then click **Create** to generate the embed token and URL.
A **live preview** renders inside the form as you configure it, so you can see the result before committing.
The embed URL is displayed immediately after creation. Copy it to your clipboard or drop it into an iframe in your product.
## Title
An optional **Title** for the embed itself. This is a label for your own reference in the embed list — distinct from **Hide Title**, which controls whether the *dashboard's* name is shown to viewers.
## Expiration
Every embed has an **expiry date** — the date and time after which the token becomes invalid and the URL stops working.
| Detail | Behaviour |
| - | - |
| **Default** | 14 days from the date of creation |
| **Configurable** | Pick any future date and time using the date picker |
| **Past dates** | Disabled — expiry must be in the future |
Once an embed token expires, anyone with the link sees an error. Create a new token to restore access.
> Set a longer expiry for permanent, public-facing embeds. Use short expiry windows for temporary previews or client reviews.
## Domain Restriction
By default, an embed is accessible to anyone with the link. You can restrict it to specific domains so it only loads when embedded on your approved sites.
Add one or more domains to the **Domains** field (e.g. `yourcompany.com`, `app.yourcompany.com`). Each domain is validated for format.
| Configuration | Behaviour |
| - | - |
| **No domains set** | Embed is publicly accessible to anyone with the URL |
| **One or more domains set** | Embed only loads when accessed from those domains |
Domains are displayed with their favicon in the embed list for easy identification.
## Display Options
Four controls sit together in the creation form:
| Control | Description |
| - | - |
| **Hide Title** | *"Hide the dashboard title in the embedded view."* Useful when your own product already provides the context. |
| **Allow Export** | *"Allow data from chart, tables and kpis to be exported."* Off by default — turn it on only if viewers should be able to download your data. |
| **Theme** | **Auto** / **Light** / **Dark**. *"Force light or dark mode in the embedded view."* On Auto the embed follows the viewer's system preference. |
| **Interactive Dashboard** | See [Interactive Dashboard](#interactive-dashboard) below. |
## Filter Configuration
If your dashboard has [Filter widgets](/dashboards/overview#filters), you can control how each one behaves in the embed.
Each filter is a **checkbox** plus an optional **preset value**, and the combination of the two produces three behaviours:
| Checkbox | Preset value | Result |
| - | - | - |
| **Checked** | — | Filter is **visible** and viewers can change it |
| **Checked** | Set | Filter is **visible**, pre-populated with your value, and viewers can change it |
| **Unchecked** | Set | Filter is **hidden but still filtering** — viewers see pre-filtered results without knowing a filter is active |
| **Unchecked** | — | Filter is **excluded** — it does not filter the data at all |
The panel moves between these for you. Setting a preset on an unchecked filter switches it from *excluded* to *hidden with preset*; clearing that preset switches it back to *excluded*.
### Preset value types
| Filter type | Preset input |
| - | - |
| **Category** | Multi-select dropdown or text |
| **Date Time** | Date preset picker plus a calendar (see below) |
| **String** | Text input |
| **Numeric / Integer** | Number input |
| **Boolean** | Toggle |
#### Date presets
Date filters offer 13 presets in four groups, plus a calendar for an explicit range:
| Group | Presets |
| - | - |
| **Lifetime** | Lifetime |
| **Recent** | Today · Yesterday · Previous week · Previous 7 days · Previous 30 days |
| **Months** | Previous month · Previous 3 months · Previous 12 months |
| **Current** | Current week · Current month · Current quarter · Current year |
> Presets are relative, so an embed set to *Previous 30 days* keeps showing the last 30 days indefinitely rather than freezing on the range that was current when you created it.
### Common filter patterns
**Lock viewers to a specific customer's data:**
Leave the filter **unchecked** and set a preset equal to that customer's ID. Viewers see only their data and cannot see or change the filter.
**Show a filter but pre-populate it:**
**Check** the filter and set a preset. Viewers start with a useful default but can adjust it.
**Turn off a filter entirely for this embed:**
Leave it **unchecked** with no preset. The filter has no effect — useful when an embed is scoped to data where the filter is irrelevant.
## Interactive Dashboard
> **Feature-gated.** Interactive Dashboard requires the interactive mode feature on your plan. An upgrade prompt is shown otherwise — see [pricing](https://supaboard.ai/pricing).
*"Ship your embedded dashboard with Stella that allows your users to interact with your dashboard."*
When enabled, an AI chat interface is available to viewers inside the embedded dashboard, so they can ask natural language questions about the data they're looking at.
Turning it on reveals a **Select Agent** dropdown listing your workspace's agents. Choose which agent answers. The agent is locked for the embed — viewers cannot switch it.
## URL Parameters
Every setting saved on an embed can be overridden per-link with a query parameter. This lets one embed token serve several contexts without creating a token for each.
| Parameter | Effect |
| - | - |
| `filter` | Applies a filter configuration, **overriding** the embed's saved filter settings |
| `environment` | Overrides which [environment](/dashboards/overview#environments) the dashboard queries |
| `theme` | Forces `light` or `dark`, overriding the saved Theme setting |
| `default_dashboard_page` | Opens a specific page of the dashboard group |
| `hide_page_selector` | Hides the page switcher |
| `hide_title` | Overrides the saved **Hide Title** setting |
### Truthiness
For the boolean parameters (`hide_page_selector`, `hide_title`):
| Value | Result |
| - | - |
| `true`, `1`, or the parameter present with no value | On |
| `false`, `0`, or the parameter absent | Off |
`hide_title` is genuinely an override, not a default: if the parameter is present it wins, regardless of what the embed was saved with. Omit it to fall back to the saved setting.
> For the `filter` and `environment` parameters — how to build the token, and what the filter payload looks like — see [Adding filters and environments to an embedded dashboard programmatically](/dashboards/embedding/add-filters-and-environments-in-embedded-dashboard-programatically).
## Managing Existing Embeds
All existing embed tokens for the current dashboard group are listed in the Embeds tab.
Each embed shows:
* **Domain icons** — the sites the embed is restricted to, or "All Domains" if unrestricted
* **Expiry date** — formatted as "Expires on `{date}`"
* **Embed URL** — truncated display of the full URL
* **Copy button** — copy the embed URL to the clipboard
* **Delete button** — permanently revoke the embed token
### Revoking an embed
Click **Delete** on any embed to revoke it immediately. The token becomes invalid and the URL stops working for everyone who has it. This cannot be undone — create a new token if access needs to be restored.
## What Viewers See
Embedded dashboards open in a clean, read-only view:
* All widgets (charts, KPIs, tables, text, images, embeds) are fully rendered
* Viewers can hover charts for tooltips and click data points to [drill through](/dashboards/overview#drilling-into-data)
* Visible filter widgets are interactive
* The dashboard title is shown or hidden per the **Hide Title** setting and any `hide_title` parameter
* Export controls appear only if **Allow Export** is on
* If Interactive Dashboard is on, an AI chat interface is available alongside the dashboard
* No edit controls, widget menus, or toolbar options are shown
* Viewers do not need a Supaboard account to open an unrestricted embed
## Permissions
Only the dashboard owner can manage its embeds. The workspace's embedding features determine which modes and controls are available. Being able to edit a shared dashboard does not grant permission to create or revoke its embed tokens.
See [Share a dashboard](/dashboards/how-to/share-a-dashboard) for ordinary workspace and link sharing.
# Group and expand table rows
Source: https://docs.supaboard.ai/dashboards/expand-rows-in-dashboard-tables
Organize a table into expandable groups so readers can move from a summary to its detail.
Group a table by a meaningful field, such as product category or month, and let readers expand a group to inspect its rows. Use this when one long table is difficult to scan.
## Before you begin
You need a table widget with the fields you want to group by and permission to edit the dashboard. The **Group** tab is available when grouping is supported for that table's data source.
## Set up row groups
1. Open the dashboard and choose **More options → Edit manually**.
2. Open the table's **Table Settings** control.
3. Choose the **Group** tab.
4. Under **Group by Field**, select **+ Add Subgroup**, then choose the field.
5. For a date field, choose the time bucket that matches the review, such as a month.
6. Add another subgroup when readers need a second level, such as category followed by month.
With multiple fields, **Nested** shows a hierarchy and **Inline** shows the grouping values together. Choose the layout that makes the relationship easiest to read.
## Add a summary
Use **Column Summarize** to choose the values to summarize and the available aggregation. For example, group by category and summarize revenue with a sum.
Check the meaning of the total. Summing monthly rates does not produce an overall rate; use an appropriate calculation when the metric is a ratio or percentage.
## Save and explore
1. Choose whether to **Expand groups by default**.
2. Select **Save Changes**, then save the dashboard edits.
3. Return to view mode and use a group's expand control to open its details.
4. Collapse the group to return to the summary.
## Keep the table readable
Use the **Schema** tab to hide columns readers do not need to see or adjust their presentation. Hiding a column changes its display; it does not remove it from the underlying data.
## Show a different follow-up question
For a separate chart or question, use the widget's **Related Questions** action. Add a related question from an existing result, configure it, and save it. Readers open available related questions from the widget's data context menu.
Row grouping and related questions solve different tasks: grouping organizes the current table, while a related question opens another view of the data.
## If you cannot expand the table
* Check that a group field has been selected and the dashboard has been saved.
* If the **Group** tab is unavailable, use [Drill Through or Records](/dashboards/how-to/explore-dashboard-data) for supported widgets.
* If a summary looks wrong, review the aggregation and the granularity of the original query.
## Expected result
Readers can scan a summary and expand the groups that need attention, without leaving the dashboard.
## Next steps
[Format the table](/dashboards/widgets/table-widget) or [use a table value to filter the dashboard](/dashboards/filters/filter-dashboards-from-table-data).
# Filtering Dashboards from Table Data
Source: https://docs.supaboard.ai/dashboards/filters/filter-dashboards-from-table-data
Right-click a table cell or row to filter your dashboard instantly, and fix columns that don't match automatically.
Use a value you are already looking at to narrow the dashboard. For example, select a customer region in a table to investigate that region across the other widgets.
## Before you begin
The dashboard must already have a filter for the source column. You need viewing access to use it; changing the table's mapping requires editing access. The table's interaction settings determine which click actions are available.
## Filter by right-clicking a table
Every table widget on your dashboard can drive the filters that already exist on that dashboard. Right-click any cell, and if a filter targets that column, you'll get a shortcut to apply it — right there, right then.
**Example:** right-click a cell in the `Status` column where the value is `Closed`. If the dashboard already has a filter on `Status`, the context menu shows:
```text theme={null}
Filter by Status = Closed
Filter by this row
─────────────────────────
Records
Drill Through
```
1. Right-click any cell in a table widget.
2. If the dashboard already has a filter for that cell's column, **Filter by \[Column] = \[value]** appears in the menu.
3. If other columns in that row also have filters, **Filter by this row** appears too — it sets every matching filter at once, in one click.
4. Every widget that uses that filter updates immediately, everywhere on the dashboard.
> **Note:** This only works with filters that already exist on the dashboard — right-clicking never creates a new one. If you don't see a **Filter by…** option for a column, add a filter for it first using [Insert Filter](/dashboards/filters/how-to-add-filters-to-your-dashboard).
### How the value gets applied
For a dropdown-style filter (like `Status`), clicking **Filter by…** **replaces** the current selection with the value you clicked — it doesn't add to whatever was already selected. For a date filter, it sets a single day as the range.
## When a column doesn't match automatically
Sometimes the column name shown in a table doesn't match its real name in your data source — for example, if the underlying query renames `status` to `s`. When that happens, right-click filtering can't tell the two are the same column. **Filter Mapping** lets you point it in the right direction, by hand, once.
> **Tip:** You'll only ever need this for renamed columns. Most tables need no setup at all — right-click filtering already works automatically whenever a column's name matches its source.
### Setting up a mapping
1. Open the table widget's **Table Settings** (the pencil icon on the widget), then switch to **Click Behaviour**. Enable **Click Behaviour** when using left-click filtering; its **Filter Mapping** section lets you connect displayed columns to source columns.
2. For the column that isn't matching, open its dropdown — real columns from your data source are listed, grouped by table.
3. Pick the real column it actually comes from. Only change columns that need an explicit mapping.
4. Click **Save Changes**, then save the dashboard. Right-click filtering on that column now works.
| Column shown in table | Maps to |
| - | - |
| `s` | `customer_address.country` |
| `order_count` | *Not mapped* |
> **Note:** Mapping is entirely manual — nothing gets guessed or auto-filled. Some columns are computed from an expression and don't have one single real column behind them; those simply stay `Not mapped`, and right-clicking them just won't offer a filter shortcut. Everything else on the table keeps working normally.
## Quick answers
**Does mapping a column create a new filter?** No. Mapping only helps right-click filtering recognize a column that already has a matching filter elsewhere on the dashboard — it never adds a new filter by itself.
**What if I map a column to the wrong source?** Open its dropdown any time and pick a different one, or choose `Not mapped` to clear it. Nothing is permanent until you click **Save Changes**.
**Do I need to map every column on the table?** No — only the ones that are renamed *and* that you actually want to use with right-click filtering. Everything else is unaffected.
**Does this affect Drill Through or Related Questions?** No, those are separate features and work exactly as they did before.
## Expected result
Selecting a mapped value updates the existing matching filters and the widgets they control. Check the filter bar to see which values were applied.
## Next steps
[Investigate a number](/dashboards/how-to/explore-dashboard-data) or [add another filter](/dashboards/filters/how-to-add-filters-to-your-dashboard).
# Add filters to a dashboard
Source: https://docs.supaboard.ai/dashboards/filters/how-to-add-filters-to-your-dashboard
Let viewers choose a date range, category, or another value without editing the dashboard.
A dashboard filter can control several widgets at once. For example, an **Order date** filter can update revenue, order count, and a monthly sales chart together.
## Before you begin
Open a dashboard containing data widgets. You need **Can create** access to add a filter, or **Can edit** to change an existing one, plus an editing role in the workspace. Know which source column represents the value you want people to choose.
## Open the filter editor
1. Open the dashboard from **Artifacts → Dashboards**.
2. Select **More options → Edit manually**.
3. Choose **Insert Filter** from the toolbar near the bottom of the canvas.
## Name the filter and choose its type
Enter **Name of Filter**, using the label viewers should see. Prefer “Order date” or “Customer region” over a database column name.
Choose **Filter Type** and, where offered, **Filter Operator**.
| Type | Use it for |
| - | - |
| **Category** | A selection of named values, such as a category or status |
| **Date Time** | A date range, month, quarter, or year |
| **String** | A text search or selection of text values |
| **Integer** | Whole numbers |
| **Numeric** | Decimal numbers |
| **Boolean** | True or false values |
For **Date Time**, choose **Date Range** when viewers should set a start and end date. For **String**, use **Fuzzy Search (Like)** for a partial text match or **Category Style (In)** for a value selection. Category-style controls can offer single or multiple selection and cascading suggestions.
## Choose the columns on each widget
The canvas shows a column selector on each compatible widget while the filter editor is open.
1. Open a widget's column selector.
2. Review the selected source columns. More than one compatible column may already be selected.
3. Keep only the column or columns that this filter should control.
4. Repeat for every widget the filter should affect.
For a revenue widget, an order-date filter usually belongs on `order_date`. Selecting `updated_at` as well would also restrict when those orders were updated. For a returns widget, decide whether the same control should use the order date or the return date; make the choice explicit in the label or dashboard notes.
A widget with no compatible columns cannot use that filter. Check its source and the selected filter type instead of choosing an unrelated column.
## Set an optional default
Use **Default Value (Optional)** to choose the starting selection. For a date range, enter **From** and **To**, or turn on **Use preset period** and choose a relative period.
Use a relative period for a dashboard that should keep opening on a recent week or month. Use fixed dates for a review of a specific event.
## Save and test
1. Select **Insert Filter**, or **Save Changes** when editing an existing filter.
2. Save any remaining dashboard edits with **Save**.
3. In view mode, choose a value in the filter bar.
4. Check each intended widget and confirm that the result changes in the way you expect.
## Using filters on your dashboard
Viewers use the controls above the widgets. Choose a value or period, then wait for the affected widgets to reload. Clear the current selection when you want to broaden the view.
Filters combine: choosing a region and a date range narrows the data by both. A widget only follows filters mapped to its relevant source columns.
## Editing an existing filter
Enter manual edit mode and open the existing filter's edit control. Change the label, type, source columns, or default, then select **Save Changes**. Recheck the affected widgets in view mode.
## Deleting a filter
Open its edit panel, select **Delete filter**, and review the confirmation. This removes the filter control, not the source data. Save the dashboard edits when prompted.
## If the result is unexpected
| What you notice | What to check |
| - | - |
| A column is missing | The widget's data source and the filter's type |
| A widget ignores the filter | Its selected source columns; the query may also intentionally exclude filtering |
| Data disappears unexpectedly | Unrelated columns selected together, conflicting filters, or a period with no matching rows |
| Suggested values are too broad | Whether cascading suggestions are enabled and supported for the connection |
## Expected result
A viewer can change the selected date or value and see the intended widgets update together.
## Next steps
[Filter by clicking a table value](/dashboards/filters/filter-dashboards-from-table-data), [keep a benchmark outside a filter](/dashboards/filters/ignore-filtering-for-a-given-table), or [configure filters for an embed](/dashboards/embedding/add-filters-and-environments-in-embedded-dashboard-programatically).
# Partial Filters
Source: https://docs.supaboard.ai/dashboards/filters/ignore-filtering-for-a-given-table
Keep parts of your query unaffected by dashboard filters — just add `$$`.
## What are Partial Filters?
When someone filters a dashboard, those filters normally apply to every table in your widget's query. But sometimes you don't want that. Maybe you're comparing this month's sales to all-time sales, and you need that all-time number to stay put no matter what filters get applied.
Partial Filters solve this. Put `$$` in front of a table reference, and that reference gets left alone. Everything else filters like normal.
> **Changed in September 2026:** the marker used to be a single `$`, and it could go anywhere in the name. It's now `$$`, and it only works as a prefix. A single `$` is treated as a normal reference and gets filtered like everything else — so if you have `$ORDERS` or `ORDERS$` in existing queries, change them to `$$ORDERS`.
## The basics
Put `$$` directly before the table name you want to protect from filters:
```sql theme={null}
SELECT COUNT(*) FROM $$ORDERS
```
That's it. The `$$` is a signal to the filtering layer — it's consumed before your query reaches the database, which only ever sees `ORDERS`.
| You write | What happens |
| - | - |
| `ORDERS` | Filtered (the default) |
| `$$ORDERS` | Not filtered |
| `$ORDERS` | Filtered — a single `$` is not a marker |
| `ORDERS$$` | Not a marker — prefix only |
Matching is case-insensitive (`$$orders` works) and binds to the whole name — `$$ORDERS_ARCHIVE` marks `ORDERS_ARCHIVE`, not `ORDERS`.
## Where to add the `$$`
Two places:
1. **Query Bench** when you're writing or editing the SQL
2. **Dashboard Edit** when you're tweaking a widget in place
Changes apply the next time the widget runs.
## Examples
### "What % of all-time orders happened this period?"
You want the numerator to follow the dashboard's date filter, but the denominator should always be the all-time total.
```sql theme={null}
WITH current_period AS (
SELECT COUNT(*) AS orders_in_period
FROM ORDERS -- filtered: reflects the selected period
),
all_time AS (
SELECT COUNT(*) AS total_orders
FROM $$ORDERS -- not filtered: always all-time
)
SELECT
ROUND(
(SELECT orders_in_period FROM current_period) * 100.0 /
NULLIF((SELECT total_orders FROM all_time), 0),
2
) AS percentage_of_total;
```
### "Show filtered revenue next to a company-wide benchmark"
```sql theme={null}
SELECT
(SELECT SUM(amount) FROM SALES) AS filtered_revenue, -- moves with filters
(SELECT AVG(amount) FROM $$SALES) AS company_average; -- stays constant
```
### Mixing filtered and unfiltered references in one query
```sql theme={null}
SELECT
u.region,
COUNT(o.id) AS filtered_orders,
ref.total_users
FROM ORDERS o -- filtered
JOIN USERS u ON u.id = o.user_id -- filtered
LEFT JOIN (
SELECT region, COUNT(*) AS total_users
FROM $$USERS -- not filtered: full population per region
GROUP BY region
) ref ON ref.region = u.region
GROUP BY u.region, ref.total_users;
```
The same table (`USERS`) appears twice and behaves differently in each spot — that's the whole point of putting the `$$` on individual *references* rather than the table itself.
## Tips
**Use it sparingly.** The default — filtering everything — is almost always what you want. Only reach for `$$` when you have a specific reason to keep a reference static.
**It's per-reference, not per-table.** If `ORDERS` shows up three times and you want two of them unfiltered, mark those two and leave the third alone.
**Leave a comment.** Future you (or a teammate) will appreciate a quick note like `-- baseline, intentionally unfiltered` next to any `$$`-marked reference.
**Schema-qualified names work too.** Put the `$$` on the table part: `analytics.$$ORDERS`.
## FAQ
**Does this work for tables, charts, and KPIs?**
Yes — same behavior across all widget types.
**What if I forget one `$` and write `$ORDERS`?**
Nothing breaks. A single `$` is dropped and the reference gets filtered like any other, so your results will move with the dashboard filters. If that reference was supposed to stay static, that's your cue to add the second `$`.
**Will the `$$` show up in query errors or results?**
No. It's consumed before the query reaches the database, so your column names, joins, and aliases all work exactly as written.
**I was using `$` before this change — do my widgets still work?**
They run, but those references are now filtered. Under the old behavior a stray `$` silently disabled filtering; that's exactly what changed. Switch any intentional opt-outs to `$$`.
# Create your first dashboard
Source: https://docs.supaboard.ai/dashboards/how-to/create-a-dashboard
Turn a business question into a dashboard, then check it before sharing.
Build a dashboard for a question your team needs to answer repeatedly. This walkthrough uses a revenue review: headline numbers, a monthly trend, a category breakdown, and a date filter.
## Before you begin
* Your workspace needs a [connected data source](/data-sources/overview).
* Choose an agent that can read the relevant tables and understands your business definitions.
* You need permission to create dashboards. Adding to an existing dashboard needs **Can create** access and an editing role in the workspace.
## Start with a clear brief
1. Open **Artifacts** in the sidebar and select **New Artifact**.
2. Choose **Dashboard** and enter a name people will recognize.
3. Describe the audience, metrics, period, and filters. Choose a project if the dashboard belongs with other work.
4. Select **Build with AI** to continue in the Analyst.
Create a dashboard for our monthly revenue review. Show net revenue, return amounts, and refunds as headline metrics; compare them by month; and add a table by product category. Use USD. Add date filters and explain which date each metric uses. Ask me if the definition of net revenue is unclear.
You can also choose **Dashboard** beneath the Home message box, or ask for a dashboard in a chat that already contains your analysis.
## Review the result
Open the dashboard card in the conversation. Check one part at a time:
1. **Definitions:** does revenue include discounts, canceled orders, or refunds?
2. **Dates:** does each metric use the intended event date?
3. **Totals:** do the headline numbers agree with the supporting tables?
4. **Filters:** does changing a value affect the intended widgets?
5. **Readability:** can a teammate understand the page without reading the chat?
Ask for specific changes in the same conversation: “Move the three headline metrics to the top,” “Use completed orders only,” or “Add a note explaining the return-date filter.”
## Build it manually instead
Choose **Build Manually** in the creation form. Use **Insert Chart**, **Insert Table**, or **Insert KPI** in the bottom toolbar, then select an existing result or describe the widget you need. Add text for definitions and [filters](/dashboards/filters/how-to-add-filters-to-your-dashboard) for the slices people will explore.
Follow [Add widgets and edit the layout](/dashboards/how-to/edit-a-dashboard) for the full editing and saving workflow.
## Expected result
The dashboard appears in **Artifacts → Dashboards** and in the selected project's **Artifacts** tab. You can reopen it and use its filters without editing its design.
## Next steps
[Check the data behind a number](/dashboards/how-to/explore-dashboard-data), then [share the dashboard](/dashboards/how-to/share-a-dashboard) with the right access level.
# Add widgets and edit a dashboard
Source: https://docs.supaboard.ai/dashboards/how-to/edit-a-dashboard
Choose between AI changes and manual editing, then save and review your dashboard.
Use AI to describe a change, or use the editor to arrange widgets and adjust their appearance yourself. A widget is one item on the dashboard: a chart, table, headline metric, text block, image, or embed.
## Before you begin
Open the dashboard from **Artifacts → Dashboards**. You need **Can edit** access to change existing content or **Can create** to add widgets and pages. Changes also require an editing role in the workspace.
## Edit with AI
Select **Edit with AI** at the top of the dashboard. The dashboard opens alongside an Analyst conversation. Describe the change and review the result in the preview.
Be specific: “Keep the existing charts, add a KPI for completed orders, and put it beside net revenue.” For questions about the current numbers, use [Insight AI](/dashboards/insights-in-dashboard).
## Open the manual editor
1. Open **More options** (**⋯**) at the top of the dashboard.
2. Select **Edit manually**.
3. The toolbar changes to **Undo**, **Redo**, **Settings**, **Cancel**, and **Save**. The insert toolbar appears near the bottom of the canvas.
## Add a widget
Choose an insert control in the bottom toolbar. Hover over an icon to see its name.
| Choose | Use it for |
| - | - |
| **Insert Chart** | Trends and comparisons |
| **Insert Table** | Detailed rows or a summary by category |
| **Insert KPI** | A headline number, such as revenue or order count |
| **Insert Filter** | A control viewers use to narrow the data |
| **Insert Text** | Titles, definitions, and commentary |
| **Insert Picture** | An image from a file |
| **Insert Embed** | Content from a URL that allows embedding |
For a chart, table, or KPI, the placeholder lets you select an existing result from a collection or create one using a prompt. Choose the result that already has the right calculation; changing its appearance does not change what the metric means.
## Arrange and format
* Drag the widget's move handle on its left edge to reposition it.
* Drag its resize handle to change its size.
* Use the widget's pencil or settings control to change its chart, table, or KPI formatting.
* Use the widget's **⋯** menu for actions such as changing its colors, hiding its header, moving it to another page, or removing it.
* Use the dashboard's **Settings** for shared colors, fonts, spacing, and other page settings.
Start with the question readers need answered. Put the headline metrics first, then the chart that explains the change, then the detailed table.
## Save and check
1. Apply changes in any open widget settings panel.
2. Select **Save** in the dashboard toolbar to save the dashboard edits.
3. Check the result in view mode, including filters and any resized tables.
Use **Undo** and **Redo** while editing. **Cancel** abandons the current dashboard edits. If you navigate away with unsaved changes, review the save-or-discard prompt before leaving.
## Expected result
The saved layout and widget settings appear when you reopen the dashboard. Viewers can explore the data without seeing editing controls.
## Next steps
Configure [charts](/dashboards/widgets/chart-widget), [tables](/dashboards/widgets/table-widget), or [KPIs](/dashboards/widgets/kpi-widget), then [add dashboard filters](/dashboards/filters/how-to-add-filters-to-your-dashboard).
# Explore the data behind a dashboard
Source: https://docs.supaboard.ai/dashboards/how-to/explore-dashboard-data
Use filters, records, drill-through, and Insight AI to understand what a metric means.
Start with the number that needs explaining, then narrow the question. You can explore a dashboard without editing its layout.
## Before you begin
You need access to view the dashboard. **Insight AI** requires **Can chat** access or higher. Drill options depend on the widget's data and interaction settings.
## Set the scope
Read the dashboard's definitions and choose its filters first. A sales dashboard can use different dates for orders, returns, and refunds; check which date each control represents.
Use the same date range and category when comparing two widgets. If one total stays unchanged, it may be designed to ignore that filter or use a different source column.
## Inspect a chart or table
1. Hover over a chart point to read the value and series.
2. Right-click a chart point or table cell to see available actions.
3. Choose **Records** to inspect the rows behind the selected result.
4. Choose **Drill Through** to break a result down by a category or time column.
5. Use the drill view's breadcrumb to return to an earlier level.
The available columns come from the widget's query. A KPI can offer **Records** without offering every chart drill option.
If right-clicking offers **Filter by…**, it applies a value to an existing dashboard filter. It does not create a new filter. See [Filter from a table](/dashboards/filters/filter-dashboards-from-table-data).
## Ask a follow-up with Insight AI
Select **Insight AI** at the top of the dashboard and ask a focused question:
* “Which category explains most of the change in net revenue?”
* “What period does this return amount cover?”
* “Show the rows behind this unusual value.”
Check the explanation against the relevant widgets and ask for the underlying breakdown if the answer needs more support. Use **Edit with AI** when you want the dashboard itself changed.
## Expected result
You can explain the period, definition, and breakdown behind the number, or identify what needs clarification before using it in a decision.
## Next steps
[Read widget insights](/dashboards/insights-in-dashboard), [export the supporting data](/dashboards/how-to/export-dashboard-reports), or ask the dashboard owner to [add a missing filter](/dashboards/filters/how-to-add-filters-to-your-dashboard).
# Export a dashboard or create a report
Source: https://docs.supaboard.ai/dashboards/how-to/export-dashboard-reports
Choose a dashboard snapshot, an AI-written report, a chart image, or a table export.
Choose the output that matches what your audience needs. A snapshot captures the dashboard as it looks; a report adds an AI-written explanation around the data.
## Before you begin
Open the intended dashboard page and check the data, period, and filters. Generating an AI report requires **Can chat** access or higher. Available actions also depend on your workspace permissions.
## Choose the output
| Output | Where to start |
| - | - |
| PDF of the dashboard's current appearance | **More options → Take a Snapshot** |
| PDF or PowerPoint with an AI-written narrative | **More options → Reports → New Report** |
| Image of one chart | The chart's **Export as PNG** control |
| Data from one table | The table's **Export as CSV** control |
| Recurring email delivery | **More options → Email Reports** |
## Take a snapshot
1. Set the dashboard filters to the view you want to share.
2. Select **More options → Take a Snapshot**.
3. Wait for the snapshot to finish, then choose **Download**.
4. Open the PDF and check that the intended page and data slice were captured.
## Create an AI report
1. Select **More options → Reports**, then **New Report**.
2. Choose the dashboard page and enter a **Report Title**.
3. Choose **PDF** or **PPT** in **Report Type**.
4. Review **Filters** in the report form. Set the intended data slice explicitly.
5. Use **Custom Instruction for AI** to describe the audience, questions, and structure.
6. Optionally use **Advanced Settings** for page-by-page guidance.
7. Select **Create Report**, wait for generation, then open and review the file.
For example: “Prepare a concise business review. State the reporting period, explain the biggest changes, and end with three questions for the team.” Avoid asking for a conclusion that the data has not established.
Reports remain available from the dashboard's **Reports** panel so you can download them again.
## Expected result
You have a downloaded file that matches the intended dashboard, period, and audience. Check figures and formatting before sharing it.
## Next steps
For a report assembled from several sources or an existing analysis, use [Reports and presentations](/artifacts/reports-and-presentations). For repeated delivery, follow [Schedule a dashboard report](/automations/schedule-dashboard-reports).
# Share a dashboard
Source: https://docs.supaboard.ai/dashboards/how-to/share-a-dashboard
Give teammates the right level of access and understand who can open a dashboard link.
Sharing lets another person open the dashboard and, if you allow it, ask questions, change existing content, or add new content.
## Before you begin
You must own the dashboard to manage its sharing. Review the data it contains and decide what recipients should be able to do.
## Share with specific people
1. Open the dashboard and select **Share**.
2. Find the person by name or email.
3. Choose the access level beside the name field.
4. Select **Share** and review **People with access**.
5. Use **Copy link** to get the dashboard address.
## Choose an access level
| Level | What the person can do |
| - | - |
| **Can view** | Open all dashboard pages, use filters, and drill into widgets |
| **Can chat** | Also ask the AI about the dashboard and generate reports |
| **Can edit** | Also change or remove existing widgets, pages, layout, and filters |
| **Can create** | Also add new widgets and pages |
Editing and creating also require an editing role in the workspace. Sharing, deleting the dashboard, and managing its embeds remain owner actions.
## Check project and general access
If the dashboard belongs to a project, **People with access** also shows access inherited from that project. Review [project sharing](/projects/overview#share-and-manage-the-project) when a person's access comes from there.
**General access** controls whether a wider audience can open it:
* **Restricted:** only people who already have access.
* **Everyone in the workspace:** workspace members receive the selected access level.
* **Anyone with the link:** people with the link receive the selected access level.
Read the description under the control after choosing an option. Copying a link does not, by itself, make a restricted dashboard accessible to its recipient.
## Expected result
The intended people appear in the access list, and the link opens with the permissions you selected. Check the recipient's access before using the dashboard in a team review.
## Next steps
Use [embedding](/dashboards/embedding/overview) to place a dashboard in another site, or [export a report](/dashboards/how-to/export-dashboard-reports) to share a file.
# Understand dashboard insights
Source: https://docs.supaboard.ai/dashboards/insights-in-dashboard
Use widget insights and Insight AI to explain a number without changing the dashboard.
Use an insight to understand what a widget shows, then follow up when you need a deeper explanation. Insights are most useful when you check their scope against the chart or table beside them.
## Before you begin
Open a dashboard you can access. Asking **Insight AI** questions requires **Can chat** access or higher. Wait for the relevant widgets to finish loading.
## Read a widget's insights
1. Find the chart, table, or KPI you want to understand.
2. Open **Insights and Info** on that widget.
3. Read the overview and the main findings.
4. Check the stated period, filters, and definitions against the widget.
A widget insight can include an **Overview**, individual **Insights**, and the **Scope** of the data used. It describes that widget's results; a claim about the whole business may need more supporting data.
## Ask Insight AI
Select **Insight AI** at the top of the dashboard to ask a question about its data. For example: “Which categories explain the decline?” or “Are returns grouped by order date or return date?”
Ask for a supporting table or breakdown when a conclusion needs checking. Continue in the same conversation so the question retains its context.
## Choose the right AI control
| You want to… | Use |
| - | - |
| Explain an existing number or trend | **Insight AI** |
| Add a chart, change a filter, or rearrange the dashboard | **Edit with AI** |
| Inspect source rows or break down a point yourself | [Records or Drill Through](/dashboards/how-to/explore-dashboard-data) |
## Expected result
You understand what the widget covers, which findings the data supports, and which questions still need investigation.
## Next steps
[Explore the underlying data](/dashboards/how-to/explore-dashboard-data) or [create a report](/dashboards/how-to/export-dashboard-reports) after checking the findings.
# Dashboards
Source: https://docs.supaboard.ai/dashboards/overview
Build, filter, and share dashboards from your business data.
A dashboard brings the numbers for a recurring question into one place. Combine headline metrics, charts, tables, and filters so your team can review performance and investigate changes.
Open **Artifacts → Dashboards** to find a dashboard. Start with a how-to guide below, or use the sections on this page as a reference.
## How-to guides
Describe the result, check the numbers, and refine the layout.
Add charts, tables, and KPIs, then save your changes.
Choose the source columns each control should affect.
Use records, drill-through, and Insight AI.
Choose who can view, chat, edit, or add content.
Download a snapshot, create a report, or schedule delivery.
## Organisation — Groups and Dashboards
Dashboards are always nested inside a **Dashboard Group**.
```text theme={null}
Dashboard Group (e.g. "Marketing")
├── Dashboard (e.g. "Campaign Overview")
└── Dashboard (e.g. "Ad Spend Breakdown")
```
A group can contain any number of dashboards, surfaced as tabs at the bottom of the screen. Sharing and access control are set on the group and apply to every dashboard in it.
## Creating a Dashboard Group
Open **Artifacts → Dashboards** to find existing dashboards. To create one, select **New Artifact → Dashboard**, enter a name, and optionally choose a project.
* **Build Manually** creates the dashboard group and opens its editor.
* **Build with AI** also needs a description. It opens the Analyst with your brief so you can build through chat.
You need access to the data you want to display and permission to create dashboards.
## Building a Dashboard With AI
### Describe what you want
Start from **Home → Dashboard**, **New Artifact**, or an existing Analyst chat. Describe the audience, metrics, time period, and any filters: “Build a weekly sales dashboard with revenue, order count, and a region filter.”
### Review the plan
Answer any clarifying questions, then open the dashboard card in the chat to inspect the result. Request changes in the same conversation. The Analyst can update widgets, filters, layout, and dashboard pages.
### Or build it yourself
Choose **Build Manually**, then use the dashboard's edit controls to add charts, tables, KPIs, text, images, and embeds. The sections below explain these controls.
## Edit Mode vs View Mode
### View mode
The default for readers. Viewers can:
* Interact with charts (hover tooltips, right-click to drill through)
* Apply dashboard filters
* Scroll and read all widget content
* Export widgets
* Open **Insight AI** if you have **Can chat** access
### Edit mode
Select **More options → Edit manually**. **Can edit** access lets you change existing content; **Can create** also lets you add widgets and pages. An editing role in the workspace is required for changes. Depending on your access, you can:
* Add, move, resize, and delete widgets
* Configure widget data and visualisation
* Change dashboard settings
* Undo and redo layout changes
### The toolbar
**View mode** offers **More options**, **Share**, **Edit with AI**, and **Insight AI**, depending on your access. Manual editing is inside **More options**.
**Edit mode** replaces them with:
| Control | Description |
| - | - |
| **Undo** / **Redo** | Step through layout history |
| **Settings** | Open [Dashboard Settings](#dashboard-settings) |
| **Cancel** | Discard changes and return to view mode |
| **Save** | Persist your changes — disabled until something has actually changed |
### Saving is explicit
Edits are **not** saved as you go. Nothing is persisted until you click **Save**.
Navigating away with unsaved work raises an **Unsaved Changes** dialog — *"You have unsaved dashboard changes. Would you like to save them before leaving?"* — with **Discard and Leave** and **Save and Leave**.
### Undo and redo
| Action | Shortcut |
| - | - |
| **Undo** | `⌘Z` |
| **Redo** | `⌘⇧Z` |
History is kept for the current browser session, up to 30 steps, and survives a page refresh mid-edit.
> The keyboard shortcuts require the **Command** key. On Windows and Linux, `Ctrl+Z` does nothing here — use the Undo and Redo buttons in the toolbar.
## Adding Widgets
In edit mode a **floating toolbar** sits at the bottom-centre of the canvas. Drag it anywhere; double-click to snap it back.
| Button | What it inserts |
| - | - |
| **Insert Picture** | An uploaded image |
| **Insert Embed** | An external URL in an iframe |
| **Insert Filter** | An interactive filter control |
| **Insert KPI** | A single headline metric |
| **Insert Table** | A paginated data table |
| **Insert Chart** | A chart |
| **Insert Text** | A rich-text block |
### Data widgets are a two-step flow
Picture, Embed, Filter, and Text insert immediately. **KPI, Table, and Chart** insert a **placeholder** that asks how you want to fill it:
| Choice | What happens |
| - | - |
| **Select From Collection** | Reuse a result from an Analyst conversation or saved query. Search for the result and select it to fill the placeholder. |
| **Create using prompt** | Describe the widget you want, pick an agent, and it's generated in place. The microphone works here too. |
This means you never have to leave the dashboard to build a widget, and anything you built in [the Analyst](/analyst/overview) or [Query Bench](/query-bench/overview) is one click from the canvas.
## Widget Types
| Widget | Purpose |
| - | - |
| **Chart** | Visualise data as bar, line, pie, scatter, and more |
| **KPI** | A single headline metric with optional trend |
| **Table** | Data in a paginated, sortable grid |
| **Filter** | Let viewers narrow every widget at once |
| **Text** | Formatted written content, headings, or annotations |
| **Image** | An uploaded image |
| **Embed** | Content from a URL that permits embedding |
### Chart
An interactive chart for comparing values or showing a trend. Configure the data source, axes, chart type, colours, legend, and labels from the visualisation panel — see [Chart Widget](/dashboards/widgets/chart-widget).
In view mode: hover for tooltips, right-click to [drill](#drilling-into-data).
**Export:** PNG, via an icon in the widget header.
### KPI
A single computed metric. Four types — number, percentage, trend, and comparison — determined by the query and analysis behind it. Formatting is configured in the **Edit KPI** panel; see [KPI Widget](/dashboards/widgets/kpi-widget).
### Table
Query results in a paginated, scrollable grid, with column grouping, aggregate footers, conditional formatting, pivots, and click-to-filter. See [Table Widget](/dashboards/widgets/table-widget).
**Export:** CSV, with a choice of all data, the current page, or a row range.
### Filter
An interactive control that filters every compatible widget at once, without entering edit mode. Filter widgets appear in a **sticky bar** at the top of the dashboard, not in the grid.
### Text, Image, and Embed
**Text** is a full rich-text editor — bold, italic, underline, strikethrough, headings, alignment, colours, links, and emoji. **Image** uploads a file that scales to its grid cell. **Embed** displays content from a URL that permits embedding.
## Filters
Filters operate at two levels.
### Widget-level filters
Configured inside an individual widget's data settings. Fixed — viewers cannot change them. Use these to permanently scope a widget, e.g. "always show only the last 30 days".
### Dashboard filters
Added to the canvas as a **Filter** widget. Viewers change these in view mode and every compatible widget updates.
#### Filter types
| Type | UI control | Operators |
| - | - | - |
| **Category** | Multi-select dropdown | — |
| **Date Time** | Date picker | Month · Quarter · Year · Date Range |
| **String** | Text or chip input | Fuzzy Search (Like) · Category Style (In) |
| **Integer** | Number input | — |
| **Numeric** | Number input | — |
| **Boolean** | Toggle | — |
#### Date presets
Date filters offer 13 presets, grouped:
| Group | Presets |
| - | - |
| **Lifetime** | Lifetime |
| **Recent** | Today · Yesterday · Previous week · Previous 7 days · Previous 30 days |
| **Months** | Previous month · Previous 3 months · Previous 12 months |
| **Current** | Current week · Current month · Current quarter · Current year |
Plus **Relative date range…**, which opens a builder: a direction (**Previous** or **Next**), a quantity, and a unit (**Days**, **Weeks**, **Months**, **Years**). Use it for windows the presets don't cover — "previous 45 days", "next 2 quarters".
#### Cascading filters
Category filters, and String filters using the *Category Style (In)* operator, offer **Cascade Suggestions based on other filters**. With it on, the filter's suggestions narrow to values that actually exist under the filters already applied — pick a country, and the city list shrinks to that country's cities.
> Cascading depends on the connector pushing the search into the database. On connectors that can't, the suggestion list stays unfiltered. See [Data Sources](/data-sources/overview#column-values-and-filter-suggestions).
## Widget Operations
From the widget header menu (the three-dot icon revealed on hover):
### In edit mode
| Operation | Description |
| - | - |
| **Convert to Chart / Table** | Switch a widget between chart and table in place. Hidden when the data has no plottable value axis. |
| **View / Edit Query** | Open the query behind the widget |
| **Widget Colors** | Set this widget's **Background**, **Border**, and **Text** colours, or reset any of them to the dashboard default |
| **Hide Header** | Hide the widget's title bar in view and embed modes |
| **Table Interaction** *(tables)* | Row hover, cell selection, click-to-filter, context menu, and column source mapping |
| **Move To** | Move the widget to another dashboard in the group |
| **Related Questions** | Attach follow-up questions viewers can jump to |
| **Delete** | Remove the widget |
### Resizing and moving
* **Move** — drag the widget by the handle on its **left edge**. Dragging the header does not move it.
* **Resize** — drag the bottom-right corner.
Widgets snap to the grid. Remember to **Save**.
## Drilling Into Data
Right-click any chart data point or table cell to open the widget menu:
| Item | What it does |
| - | - |
| **Related Questions** | Jump to a follow-up question attached to this widget |
| **Records** | Show the raw rows behind the point you clicked |
| **Drill Through** | Break the metric down by another dimension |
**Drill Through** opens a **Drill by** submenu offering **Category** or **Time**, then the specific columns available from each table in the query.
The drill view has a breadcrumb of steps so you can see the path you took and step back up it, plus date range controls for time-series drilling. Dashboard filters are carried into the drilled view, so you're always drilling the slice you were looking at.
> **Records** is available on KPI widgets; **Drill Through** is not. A KPI is already a single aggregate, so there's no dimension left to break out — but you can still see the rows behind the number.
Left-clicking a table cell does something different: it [filters the dashboard](#click-to-filter) by that value.
### Click-to-filter
On tables with **Click Behaviour** enabled, left-clicking a cell pushes its value into any matching dashboard filter and highlights the row. Columns need to be mapped to their underlying source for this to work — see [Table Widget](/dashboards/widgets/table-widget).
## Dashboard Settings
In edit mode, click **Settings** to open the **Dashboard Settings** panel. Everything here applies to the whole dashboard.
| Setting | What it controls |
| - | - |
| **Color** | Dashboard background colour |
| **Widget Colors** | Default **Widget Background**, **Border**, and **Text** colours. Individual widgets can override these. |
| **Table Interaction** | Default interaction behaviour for table widgets |
| **Font Style** | Default, Sans-Serif, Serif, Monospace, Fantasy, Cursive, System UI — or paste a **Google Fonts URL** and the family is extracted for you |
| **Environment** | Which [environment](#environments) this dashboard queries |
| **Width** | Canvas width |
| **Widget Spacing** | Tight, Default, Wide, or a custom value |
| **Widget Border Radius** | 0–32 px, default 8 |
| **Default Agent** | Which agent answers **Insight AI** questions on this dashboard |
| **Auto-refresh** | How often widgets re-query on their own |
| **Access Control** | See [Access control](#access-control) |
## Environments
An **environment** remaps which resource and database a dashboard's widgets query, without touching the widgets themselves. The usual case is one dashboard serving staging and production, or the same dashboard pointed at different customers' databases.
Manage them from **Settings → Environment**, where you can create environments and map each widget's tables to the target source. Switching environments needs no rebuild — the widgets are unchanged; only where they read from moves.
Embedded dashboards can be pinned to an environment per link — see [Embed URL parameters](/dashboards/embedding/overview#url-parameters).
## The Share Menu
Click **More options** (**⋯**) in the view-mode toolbar. Some actions require ownership or additional access.
| Item | What it does |
| - | - |
| **Share** | Manage people, project access, and general access — see [Share a dashboard](/dashboards/how-to/share-a-dashboard) |
| **Create as Datasite** | Turn this dashboard into an AI-built [datasite](/datasites/overview), seeded with its widgets |
| **Embed** | Manage embed tokens — see [Dashboard Embeds](/dashboards/embedding/overview) *(web only)* |
| **Reports** | Generate a PDF or PowerPoint report — see below |
| **Take a Snapshot** | A quick PDF of the dashboard as it looks now, no AI involved |
| **Email Reports** | Schedule a recurring report by email — see below |
### Take a Snapshot
A one-click PDF export. Supaboard shows *"Preparing snapshot…"*, then *"Snapshot ready"* with a **Download** action. Whatever filters you currently have applied are captured in the snapshot.
Use this when you want the dashboard exactly as it is. Use **Reports** when you want an AI-written narrative around the data.
### Reports
Open **Reports**, then select **New Report**:
| Field | Notes |
| - | - |
| **Report Title** | Name of the generated report |
| **Report Type** | **PDF** or **PPT** |
| Dashboard page | Which page of the group to report on |
| Filter settings | Which data slice to report on |
| **Custom Instruction for AI** | Tell the AI what to emphasise, the tone, or how to structure the narrative |
| **Advanced Settings** | Set a fixed number of pages (0–20) and write a description per page |
Past reports are listed in the panel — click any entry to download it again.
### Email Reports
Schedule a recurring report to a list of recipients.
| Field | Options |
| - | - |
| **Delivery Frequency** | Hourly · Daily · Weekly · Monthly, with day-of-week and a timezone |
| **Format** | Text mail · PDF · PPT · Excel |
| **Recipients** | Email addresses |
| Filters | The data slice to report on |
| Custom instruction | Steer the AI's narrative |
> This builds a real [automation](/automations/overview) behind the scenes. You can open it in the Automations editor afterwards to add steps, change the trigger, or branch on a condition.
## Access Control
Access is set for the dashboard group and applies to its pages. The owner can select **Share** to review people, inherited project access, and general access.
| Access | What it allows |
| - | - |
| **Can view** | Open pages, use filters, and drill into widgets |
| **Can chat** | Also ask Insight AI questions and generate reports |
| **Can edit** | Also change and remove existing content |
| **Can create** | Also add widgets and pages |
Changing content also requires an editing role in the workspace. Only the owner can manage sharing, delete the group, or manage its embeds. See [Share a dashboard](/dashboards/how-to/share-a-dashboard) for the full workflow.
## Insight AI
Click **Insight AI** in the toolbar to open a chat panel beside the dashboard. Ask questions about what you're looking at — *"why did revenue drop in March?"* — and the agent answers using the dashboard's widget data.
If a question can't be answered from the existing widgets — *"break this down by company"* — the agent falls back to running a live query against your connected database rather than giving up. It still prefers your existing widgets whenever they cover the question.
The agent used is set in **Settings → Default Agent**.
## Dashboard Tabs (Multi-page)
Multiple dashboards in a group appear as **tabs at the bottom** of the screen.
| Action | How |
| - | - |
| **Switch dashboard** | Click any tab |
| **Add dashboard** | Click the **+** on the **left** of the tab bar, enter a **Page Name**, and click **Create Page** |
| **Rename dashboard** | Right-click the tab → **Rename**, or use the selected tab's **…** menu |
| **Delete dashboard** | Right-click the tab → **Delete**, or the **…** menu |
| **Reorder tabs** | Drag tabs left or right *(edit mode only)* |
Rename and delete work in view mode as well as edit mode. The last remaining dashboard in a group cannot be deleted.
## Duplicating a Dashboard Group
From the dashboard card in **Artifacts → Dashboards**, open the action menu and select **Duplicate**.
The modal lets you **remap data sources** — point the copy at a different connection from the original. Useful for environment-specific or client-specific copies. The duplicate is named with " Copy" appended and contains every dashboard, widget, and layout setting from the original.
> For switching data sources on an *existing* dashboard rather than a copy, use [Environments](#environments) instead — no duplication needed.
## Deleting
### A single dashboard (tab)
Right-click its tab and select **Delete**, or use the tab's **…** menu. Not available if it is the last dashboard in the group.
### A dashboard group
From the dashboard card in **Artifacts → Dashboards**, open the action menu and select **Delete**. This removes the group and every dashboard and widget in it. **This cannot be undone.**
## Permissions
What you can do depends on your access to this dashboard and your workspace permissions. **Can view**, **Can chat**, **Can edit**, and **Can create** serve different tasks; an editing role in the workspace alone does not grant ownership of every dashboard.
If a control is missing, ask the owner to check **Share**. See [workspace access](/workspace/overview#roles-and-permissions) and [dashboard sharing](/dashboards/how-to/share-a-dashboard).
## Next steps
Start with [Create a dashboard](/dashboards/how-to/create-a-dashboard), then [add filters](/dashboards/filters/how-to-add-filters-to-your-dashboard) and [share the result](/dashboards/how-to/share-a-dashboard).
# Chart Widget
Source: https://docs.supaboard.ai/dashboards/widgets/chart-widget
The Chart widget visualises query results as interactive graphics. Choose a chart type that fits the columns returned by the query. Some visualizations need a different result shape.
## Before you begin
You need a chart backed by query results and permission to edit it. For a dashboard, open **More options → Edit manually**, then use the chart's menu to open its customization controls.
## Customize a chart
1. Choose a chart type suited to the comparison: a line for change over time, a bar for categories, or a scatter plot for a relationship between measures.
2. Check which fields supply the axes and series.
3. Set a clear title, labels, units, and legend.
4. Review the preview, save the widget changes, then save the dashboard.
The expected result is a chart whose title, fields, and units match the question. See [Edit a dashboard](/dashboards/how-to/edit-a-dashboard) for the full workflow.
## Reading the data
Supaboard maps query columns to chart dimensions automatically:
* The **first non-numeric column** becomes the X-axis (categories or time values).
* **Numeric columns** become series on the Y-axis.
* For charts that need a third dimension (bubble, scatter, heatmap), a second non-numeric column becomes the grouping field.
If the automatic mapping doesn't match your intent, use the **X-axis → Axis** and **Y-axis** field selectors in the settings panel to override it.
Check how missing values appear in the preview; do not treat a missing value as a measured zero.
## Chart types
Select a chart type from the **Chart type** picker in the settings panel.
### Trend over time
| Type | Best for |
| - | - |
| **Line** | A single metric over time |
| **Multiple line** | Comparing multiple metrics over time |
| **Area** | Showing volume or cumulative growth |
| **Multiple area** | Comparing multiple area series |
### Part-to-whole
| Type | Best for |
| - | - |
| **Pie** | Simple proportions (fewer than 7 slices) |
| **Donut** | Proportions with a center metric |
| **Treemap** | Nested proportions by hierarchy |
| **Sunburst** | Nested proportions as concentric rings |
| **Funnel** | Drop-off across sequential stages |
| **Grouped funnel** | Comparing funnels side-by-side |
### Comparison
| Type | Best for |
| - | - |
| **Bar** | Horizontal ranking of categories |
| **Column** | Vertical ranking of categories |
| **Grouped bar** | Side-by-side bars for multiple series |
| **Grouped column** | Side-by-side columns for multiple series |
| **Stacked bar** | Horizontal absolute totals by segment |
| **Stacked column** | Vertical absolute totals by segment |
| **Stacked 100% bar** | Horizontal proportional share per category |
| **Stacked 100% column** | Vertical proportional share per category |
| **Radar** | Multi-variable comparison on a spider chart |
### Distribution and correlation
| Type | Best for |
| - | - |
| **Scatter** | Correlation between two numeric variables |
| **Grouped scatter** | Correlation segmented by a category |
| **Bubble** | Correlation with a third magnitude dimension |
| **Heatmap** | Density or intensity across a 2D grid |
### Specialised
| Type | Best for |
| - | - |
| **Combo** | Mixing bar/column with a line on one chart |
| **Waterfall** | Cumulative contribution of sequential values |
| **Sankey** | Flow and distribution between categories |
| **Gauge** | Single value against a defined range |
| **Map** | Geographic data plotted on a map |
## Basic settings
These settings apply to all chart types.
| Setting | Description |
| - | - |
| **Title** | Display title shown above the chart |
| **Description** | Optional subtitle below the title |
| **Font size** | Base font size for axis labels and legends |
## X-axis
Controls the horizontal axis (or the category axis for horizontal bar charts).
| Setting | Description |
| - | - |
| **Axis field** | The query column used as X-axis categories |
| **Sort** | Sort categories: `None`, `Ascending`, or `Descending` |
| **Axis label** | Toggle and text for the axis title label |
| **Category labels** | Toggle the per-category tick labels on the axis |
| **Axis line** | Show or hide the axis line itself |
| **Label rotation** | Rotate category labels: `0°`, `30°`, `45°`, `60°`, or `90°` |
### Time-based X-axis
When your X-axis contains dates or timestamps, enable **Timestamp mode** to unlock:
| Setting | Description |
| - | - |
| **Date unit** | Aggregate or group by `Day`, `Week`, `Month`, `Quarter`, or `Year` |
| **Timestamp sort** | Sort chronologically: `Ascending` or `Descending` |
| **Date format** | Choose a preset format or enter a custom format string (e.g. `YYYY-MM-DD`, `MMM YYYY`) |
### Custom range
| Setting | Description |
| - | - |
| **Custom range** | Enable a fixed minimum and maximum for the X-axis |
| **Show range break** | Display a break symbol (≈) when the axis skips a discontinuous range |
## Y-axis
Controls the vertical axis (or the value axis for horizontal bar charts).
| Setting | Description |
| - | - |
| **Axis label** | Toggle and text for the Y-axis title |
| **Axis tick labels** | Toggle the numeric value labels on the axis |
| **Axis line** | Show or hide the axis line |
| **Auto range** | Set a custom minimum and/or maximum for the axis |
| **Scale** | `Linear` (default), `Log` (logarithmic), or `Power` |
| **Split axes** | Automatically split into multiple Y-axes when series have very different scales |
### Value formatting
Y-axis tick labels can be formatted independently of the raw data:
| Setting | Description |
| - | - |
| **Style** | `Number`, `Percent`, `Scientific`, or `Currency` |
| **Separator style** | `100,000,000` (Western) or `1,00,00,000` (Indian) |
| **Decimal places** | Number of decimal places to show on the axis |
| **Multiply by** | Scale factor applied before display (e.g. enter `0.001` to show values in thousands) |
| **Prefix** | Text prepended to every axis label |
| **Suffix** | Text appended to every axis label |
## Dual axis
For multi-series charts (line, bar, column, combo), a second Y-axis can be added to plot series with different units or scales on the same chart.
| Setting | Description |
| - | - |
| **Enable dual axis** | Toggle a second Y-axis on the right side |
| **Left axis label** | Label for the primary (left) Y-axis |
| **Right axis label** | Label for the secondary (right) Y-axis |
| **Series assignment** | Drag each series to the left or right axis |
| **Left tick labels** | Show/hide tick labels on the left axis |
| **Right tick labels** | Show/hide tick labels on the right axis |
## Series colors
Each series has a color swatch in the settings panel. Click any swatch to open a color picker and assign a custom hex color to that series. Colors are saved per-series by name — if the query returns the same column name on the next refresh, it retains its color.
## Data point labels
Show the actual value on each data point or bar. Available for bar, column, pie, donut, area, bubble, scatter, and radar charts.
| Setting | Description |
| - | - |
| **Show labels** | Toggle value labels on data points |
| **Position** | `Top`, `Middle`, `Bottom`, `Inside`, or `Outside` (options vary by chart type) |
| **Angle** | Rotate the label text |
| **Format** | Compact notation: `None`, `K` (thousands), `M` (millions), `B` (billions), `T` (trillions) |
| **Prefix** | Text before the value |
| **Suffix** | Text after the value |
| **Color** | Label text color |
Labels can be configured **per series** — expand a specific series under **Data points → Series** to override the global settings for that series only.
## Grid lines and goal line
### Grid lines
| Setting | Description |
| - | - |
| **Show grid lines** | Toggle background grid lines |
| **Layout** | `Horizontal` (default), `Vertical`, or `Both` |
### Goal line
A goal line draws a horizontal reference line across the chart at a specific value — useful for targets, SLAs, or budgets.
| Setting | Description |
| - | - |
| **Show goal line** | Toggle the goal line |
| **Value** | The Y-axis value where the line is drawn |
| **Label** | Text label shown alongside the line (e.g. `Target`, `Budget`) |
| **Color** | Line color (defaults to a muted accent if not set) |
## Legend
| Setting | Description |
| - | - |
| **Show legend** | Toggle the series legend |
| **Position** | `Top`, `Bottom`, `Left`, or `Right` |
Clicking a legend item in view mode toggles that series on and off interactively — this is a viewer-side interaction and does not change the saved configuration.
## Data zoom
**Data zoom** adds a scrollable range slider below the chart, letting viewers pan and zoom into a subset of the data without changing the query.
Available on all chart types except funnel and radar. Toggle it under **Settings → Data zoom**.
## Pie and donut options
In addition to the standard chart settings, pie and donut charts have:
### Center total (donut only)
| Setting | Description |
| - | - |
| **Show total** | Display the sum of all slices in the donut center |
| **Style** | `Number`, `Percent`, `Scientific`, or `Currency` |
| **Decimal places** | Precision of the center value |
| **Prefix / Suffix** | Text around the center value |
| **Multiply by** | Scale factor before display |
### Percentage labels
| Setting | Description |
| - | - |
| **Percentage display** | `Off`, `In legend`, `On chart`, or `Both` |
| **Decimal places** | Precision of the percentage shown |
| **Minimum slice %** | Hide the label on slices smaller than this percentage (avoids overcrowding) |
# Format a KPI widget
Source: https://docs.supaboard.ai/dashboards/widgets/kpi-widget
Make a headline metric readable with a clear label, units, and number format.
The KPI widget surfaces a single headline metric — a number, percentage, trend, or comparison — pulled directly from a query result. It is designed to be read at a glance.
## Before you begin
Add a KPI using the [dashboard editor](/dashboards/how-to/edit-a-dashboard) or reuse a result from the Analyst. You need editing access to change its appearance. Check that the underlying result measures the intended thing before changing its format.
## Format the metric
1. Enter **More options → Edit manually** on the dashboard.
2. Open the KPI's **Edit KPI** control.
3. In **Customize**, set a clear **Title** and choose the **Format**.
4. For a currency, select the currency and symbol position. Choose compact notation and decimal places to suit your audience.
5. Review the preview, select **Save Changes**, then save the dashboard edits.
## Reading the data
A KPI shows a prepared metric result. Check its definition, filters, and period before formatting it. A missing value is different from a genuine zero; investigate the source rather than adding a display prefix or changing precision to make it look complete.
## KPI types
A result can be a number, percentage, trend, or comparison. These describe the result itself. The **Format** control changes how its values are displayed; it does not turn a single number into a meaningful comparison with a previous period.
Ask the Analyst for the calculation you need, such as “Show completed-order revenue for last month and compare it with the month before.”
## Display options
Use **Title** for a label the audience understands. Add a **Prefix**, **Suffix**, or **Image/Emoji** where helpful, and check the preview for duplicate units such as a manually entered dollar sign next to a currency format.
## Number formatting
Choose the format that matches the meaning of the value:
| Format | Example use |
| - | - |
| **Number** | Order count or a quantity |
| **Currency** | Revenue, cost, or refunds; choose the currency and symbol position |
| **Percentage** | A rate; check whether the input is already scaled as a percentage |
| **Time (Duration)** | Elapsed time; choose the correct input unit |
| **Timestamp** | A date or point in time |
| **Weight**, **Volume**, **Length / Distance**, **Data Size** | A measured quantity with the appropriate units |
| **Ratio / Multiplier**, **Score / Rating** | A comparison ratio or score on a known scale |
| **Auto** | Let the available value determine the default presentation |
The options beneath **Format** change with your selection. For example, currency formatting offers compact notation, decimal places, separators, and negative-number presentation.
Changing a currency label does not perform an exchange-rate conversion. A rate stored as `0.25` needs different percentage handling from one stored as `25`; confirm the input scale in the preview.
## Compact notation
Use compact notation when a headline number would otherwise be difficult to scan. **Auto (Smart)** can show a large revenue value in millions; choose an unabridged display when the exact total matters. Use decimal places consistently across comparable KPIs.
## Show a trend or comparison
Ask for a trend or comparison result with an explicit time period and baseline. Review the direction of the change: an increase in revenue and an increase in refunds do not mean the same thing. Use a [chart](/dashboards/widgets/chart-widget) alongside the KPI when readers need the full timeline.
## Icon
Use **Image/Emoji** as an optional visual cue. Keep the title and units readable without relying on the icon.
## Expected result
The metric has an understandable title and unit, and the displayed precision suits the decision. Number formatting changes presentation; it does not correct an incorrect calculation.
## Next steps
[Add a date filter](/dashboards/filters/how-to-add-filters-to-your-dashboard) and check whether the KPI should respond to it, or [inspect the underlying data](/dashboards/how-to/explore-dashboard-data).
# Format and organize a table
Source: https://docs.supaboard.ai/dashboards/widgets/table-widget
Make detailed data easier to read with sorting, column formatting, grouping, and interaction controls.
The Table widget displays query results as a structured grid — rows and columns with full control over how each column looks, sorts, and highlights values.
## Before you begin
Add a table using the [dashboard editor](/dashboards/how-to/edit-a-dashboard) or reuse a result from the Analyst or Query Bench. You need editing access to save table settings.
## Open table settings
1. Choose **More options → Edit manually** on the dashboard.
2. Open the widget's **Table Settings** control.
3. Use **Settings** for the title, default sorting, and overall style; **Schema** for individual columns; and **Group**, **Pivot**, or **Click Behaviour** where available.
4. Review the preview, select **Save Changes**, then save the dashboard edits.
## Reading the data
Each row in the table corresponds to one row returned by the underlying query. Column headers are the field names from your query — you can rename them inside the widget without changing the query itself.
The table paginates automatically for large result sets. The total row count is shown in the widget footer.
**Null values** mean data is missing. Do not interpret them as zero. Check the underlying query if the distinction matters to your analysis.
**Numbers, dates, and text** are all displayed as returned by the database. Use [column controls](#column-controls) to add prefixes, suffixes, or formatting on top of the raw values.
## Sorting and filtering
Open the widget settings to set a **default sort**:
| Setting | Description |
| - | - |
| **Sort column** | The column to sort by when the widget first loads |
| **Sort direction** | `Ascending` or `Descending` |
Viewers can click any column header to sort interactively — this overrides the default sort for their session without changing the saved configuration.
**Filter data** lets you apply a persistent filter condition to the query results (e.g. show only rows where `status = "active"`). This filter is applied before the table renders and is not visible to viewers as a control.
## Column controls
Open **Schema** in the table settings and expand the column you want to configure. Column controls and available formats depend on its data type.
| Option | Description |
| - | - |
| **Rename** | Change the display label shown in the header |
| **Width** | Set a fixed pixel width for the column |
| **Prefix** | Text prepended to every value in the column (e.g. `$`, `€`) |
| **Suffix** | Text appended to every value (e.g. `%`, `ms`, `units`) |
| **Show header icon** | Display a small icon in the column header |
Prefix and suffix are purely visual — they do not change the underlying value used in sorts or calculations.
## Conditional formatting
Conditional formatting lets you apply styles to individual cells based on their value. Open the column's conditional-formatting control from the table settings or its header menu.
### Single color rules
Rules are evaluated top-to-bottom. The first matching rule wins.
Each rule has a **condition** and a **style** to apply when that condition is true.
**Condition types:**
| Condition | Applies when… |
| - | - |
| **Is empty** | Cell has no value |
| **Is not empty** | Cell has any value |
| **Equals** | Value exactly matches the given input |
| **Not equals** | Value does not match |
| **Greater than** | Numeric value exceeds the threshold |
| **Greater than or equal** | Numeric value meets or exceeds the threshold |
| **Less than** | Numeric value is below the threshold |
| **Less than or equal** | Numeric value is at or below the threshold |
| **Between** | Numeric value falls between two thresholds (inclusive) |
| **Text contains** | String value includes the given text |
| **Text does not contain** | String value does not include the given text |
| **Text starts with** | String value begins with the given text |
| **Text ends with** | String value ends with the given text |
**Style options per rule:**
| Style | Description |
| - | - |
| **Text color** | Font color (any hex, RGB, or HSL value) |
| **Background color** | Cell background color |
| **Bold** | Bold text |
| **Italic** | Italic text |
| **Underline** | Underlined text |
| **Strikethrough** | Struck-through text |
Any combination of styles can be applied in a single rule.
## Color scale
The **Scale** tab in conditional formatting applies a continuous color gradient across the column's values — useful for quickly spotting high and low values in numeric columns.
Configure three anchor points:
| Anchor | Default type | Description |
| - | - | - |
| **Min** | Minimum value in the column | Sets the color for the lowest value |
| **Mid** | *(optional)* | Midpoint color — leave blank for a two-color gradient |
| **Max** | Maximum value in the column | Sets the color for the highest value |
**Anchor types:**
| Type | Description |
| - | - |
| **Min / Max** | Automatically uses the column's actual minimum or maximum |
| **Number** | A specific absolute value |
| **Percent** | A percentage of the column's range (0–100) |
| **Percentile** | The nth percentile of values in the column |
Values between anchor points receive interpolated colors. Values outside the defined range receive the nearest anchor color.
## Table style
Open **Settings → Table Styling** to pick a preset, wrap header titles, or wrap long cell text.
| Style | Appearance |
| - | - |
| **None** | Plain table with no background colors |
| **Blue** | Blue header with light blue row banding |
| **Orange** | Orange header with warm banding |
| **Grey / Slate** | Grey header with neutral banding |
| **Yellow / Amber** | Yellow header with warm banding |
| **Green** | Green header with green banding |
| **Custom** | Choose your own header and banding colors |
### Custom style
Choose **Custom** to set header and banding colors. Review the table in both themes; the controls edit the colors for the theme currently shown.
## Pinned columns
**Pinned columns** stay fixed at the left edge of the table while the remaining columns scroll horizontally. Useful for identifier columns (e.g. name, ID) that viewers need to keep visible.
Use the column header's pin action to keep an identifier such as a customer name visible while you scroll across the table. Review the result on a narrow screen so pinned columns do not take up all the available space.
## Column order
Arrange columns in the order readers need them, with identifiers before measures. Check the table preview and save the settings when the order is ready.
## Group, pivot, and click behaviour
Use **Group** to organize rows into expandable summaries; see [Group and expand rows](/dashboards/expand-rows-in-dashboard-tables). Use **Pivot** when you want a cross-tab comparison instead of a long list.
**Click Behaviour** controls row highlighting, cell selection, and whether clicks apply dashboard filters. It also contains the **Context Menu**, **Records**, and **Drill Through** controls. **Filter Mapping** connects renamed display columns to their actual source columns.
## Expected result
The table opens with readable labels, useful number formats, and the intended default sort. Readers can find the relevant rows and use the interactions you enabled.
## Next steps
[Filter from a table value](/dashboards/filters/filter-dashboards-from-table-data) or [export table data](/dashboards/how-to/export-dashboard-reports).
# Connect a data source
Source: https://docs.supaboard.ai/data-sources/connect-a-data-source
Add a connection, confirm its tables are available, and make it usable by an agent.
Connect the database or app that holds the information you want to analyze. Once the connection is ready, Supaboard can use it in questions, queries, and artifacts.
## Before you begin
You need permission to add data sources. For a database, ask the person who manages it for its connection details and the approved network access method. Use the [connector-specific guide](/data-sources/overview#available-connectors) for required fields and permissions.
## 1. Choose the connector
Open **Data sources → Connect Data Source**. Search for your database or app, or browse **Databases** and **Apps & Services**.
## 2. Complete setup
Give the connection a name your team will recognize. For a database, enter its connection URL or the requested host, database, username, and password. Configure SSL and any tunnel or VPN required by your network.
The database form shows Supaboard IP addresses to allow in your firewall. Copy the current list from the form rather than using addresses from a screenshot.
For an app connector, follow its authorization and synchronization flow. Check the connector's guide for availability and setup requirements.
## 3. Connect and wait for discovery
Choose the connector's **Connect** button. Supaboard checks the connection and discovers its schema—the tables and columns it can use. Some sources need time for an initial sync or schema preparation.
If setup fails, check the error against the connector's troubleshooting section before trying again.
## 4. Check the tables and agent access
Open the new source and confirm the tables you expect are present. In **Settings → Connections**, review its connection details, table context, and instructions when needed.
Make sure the agent you will use has access to this source. A working connection does not override a teammate's resource restrictions or an agent's data scope.
## Expected result
The source appears in **Data sources**, the intended tables are available, and your chosen agent can answer a simple question using them.
## Next steps
[Connect PostgreSQL](/data-sources/connectors/postgres), [set up an agent](/agents/set-up-an-agent), or [ask your first question](/getting-started/quickstart).
# BigQuery
Source: https://docs.supaboard.ai/data-sources/connectors/big-query
Connect Supaboard to Google BigQuery using either OAuth (Google sign-in) or a Service Account JSON key.
Open **Data sources → Connect Data Source → Bigquery** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**IAM permissions** — The Google identity or service account you use needs:
* `BigQuery Data Viewer` — to read datasets and tables
* `BigQuery Job User` — to run query jobs
Both roles are required. `BigQuery Data Viewer` alone does not allow running queries.
**Network access** — BigQuery is a managed Google service accessible over the public internet. No firewall configuration is required.
## Authentication methods
### OAuth (Google sign-in)
The simplest method. Supaboard opens a Google sign-in flow in your browser. The connection is tied to your personal Google account and inherits that account's BigQuery permissions.
**Best for:** Individual analysts with existing BigQuery access via their Google account.
### Service Account JSON
A service account key file grants Supaboard its own identity in GCP, independent of any individual user. The connection remains valid even if team members leave.
**Best for:** Shared workspace connections, automated pipelines, production environments.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Dataset** | — | Yes (after auth) | BigQuery dataset to connect to; auto-discovers available datasets |
Authentication fields depend on the selected method:
**OAuth** — no additional fields; click **Authenticate with Google** to complete sign-in.
**Service Account JSON** — upload or paste your service account key file (JSON format).
## Finding your connection details
### Google Cloud IAM Console
**Creating a service account and downloading the JSON key:**
1. Open [IAM & Admin → Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts) in the Google Cloud Console.
2. Click **Create Service Account**.
3. Give it a name (e.g. `supaboard-bigquery`) and click **Create and Continue**.
4. On the **Grant this service account access to project** step, add two roles:
* `BigQuery Data Viewer`
* `BigQuery Job User`
5. Click **Done**.
6. Click on the new service account, then go to the **Keys** tab.
7. Click **Add Key → Create new key → JSON → Create**.
8. The JSON key file downloads automatically — keep it secure.
9. Upload this file in the Supaboard BigQuery connector form.
**Documentation:** [Create and manage service account keys](https://cloud.google.com/iam/docs/keys-create-delete) · [BigQuery predefined roles](https://cloud.google.com/bigquery/docs/access-control#bigquery)
**Finding your Dataset ID:**
1. Open [BigQuery Studio](https://console.cloud.google.com/bigquery) in the Cloud Console.
2. In the Explorer panel, expand your project.
3. Dataset names are listed directly under the project — these are the values to use in the **Dataset** field.
> Service account keys are long-lived credentials. Store the downloaded JSON file securely and rotate it periodically under **IAM & Admin → Service Accounts → Keys**.
## Recommended database user permissions
**Documentation:** [BigQuery access control overview](https://cloud.google.com/bigquery/docs/access-control) · [Dataset-level access controls](https://cloud.google.com/bigquery/docs/dataset-access-controls)
For a service account, assign the following IAM roles at the project level (or dataset level for finer-grained control):
| Role | Purpose |
| - | - |
| `roles/bigquery.dataViewer` | Read access to datasets and tables |
| `roles/bigquery.jobUser` | Permission to run query jobs |
To restrict access to a specific dataset rather than the whole project:
1. Open BigQuery Studio and click on the dataset.
2. Go to **Sharing → Permissions**.
3. Click **Add Principal**, enter the service account email, and assign `BigQuery Data Viewer`.
You still need `BigQuery Job User` at the **project** level for query execution.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `Access Denied: BigQuery: User does not have bigquery.jobs.create` | Missing `BigQuery Job User` role | Add `roles/bigquery.jobUser` to the service account at the project level |
| `Access Denied: Table` | Service account lacks dataset read access | Add `BigQuery Data Viewer` to the dataset or project |
| `Invalid key` | Wrong or malformed JSON key file | Re-download the JSON key from GCP; ensure the file is complete |
| `Project not found` | Project ID in the JSON key doesn't exist or was deleted | Verify the project is active in the GCP Console |
| `Dataset not found` | Dataset ID typo or wrong project | Check dataset name in BigQuery Studio; dataset names are case-sensitive |
# Clickhouse
Source: https://docs.supaboard.ai/data-sources/connectors/clickhouse
Connect Supaboard to a ClickHouse database for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → ClickHouse** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supaboard connects from a fixed set of egress IPs. Add them to your ClickHouse server or ClickHouse Cloud service's IP allow-list before attempting a connection. The current IP list is shown inside the connector form under **Whitelist IPs**.
**Database user permissions** — The user you provide needs at minimum:
* `SELECT` privilege on the target database and tables
See [Recommended database user permissions](#recommended-database-user-permissions) for the exact SQL.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | `localhost` | Yes | Hostname or IP of your ClickHouse server |
| **Port** | `9000` | Yes | ClickHouse native TCP port (see note below) |
| **Username** | — | No | ClickHouse user; defaults to `default` if left blank |
| **Password** | — | No | Password for the ClickHouse user |
| **Database** | — | Yes | Database name to connect to |
> **Port note** — Supaboard connects over the native TCP protocol on port `9000`. This is different from the HTTP interface (port `8123`) and the HTTPS interface (port `8443`). ClickHouse Cloud uses port `9440` for secure native TCP — check your Cloud service details if `9000` does not connect.
## Finding your connection details
### ClickHouse Cloud
1. Open [clickhouse.cloud](https://clickhouse.cloud) and select your service.
2. On the service detail page, go to **Connect**.
3. Select **Native** as the connection method.
4. Copy the **Host** (format: `xxxxx.clickhouse.cloud`) and **Port** (typically `9440` for secure native TCP on ClickHouse Cloud).
5. Use the **default** user or a custom user you have created.
6. Under **Security → IP Access List**, add each Supaboard egress IP to allow connections.
> ClickHouse Cloud uses port `9440` (TLS-enabled native TCP), not `9000`. Update the **Port** field accordingly.
**Documentation:** [ClickHouse Cloud connection details](https://clickhouse.com/docs/en/cloud/get-started/query-endpoints) · [IP access list](https://clickhouse.com/docs/en/cloud/security/setting-ip-filters)
### Self-hosted ClickHouse
1. Your **Host** is the server's IP address or hostname.
2. The default native TCP **Port** is `9000`.
3. Check `/etc/clickhouse-server/config.xml` (or `config.d/`) for any custom port configuration.
4. Ensure the server's firewall allows inbound TCP on port `9000` from Supaboard egress IPs.
**Documentation:** [ClickHouse server configuration](https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings) · [Access control and account management](https://clickhouse.com/docs/en/operations/access-rights)
## IPsec Site-to-Site VPN
If your database lives on a private network, you can peer your VPN gateway with Supaboard over IKEv2 IPsec instead of exposing the database or running a bastion host. Enable the **Use IPsec site-to-site VPN** toggle in the connector form.
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway — or a per-server mapping for providers with one tunnel endpoint per peer (OCI/AWS managed VPN), e.g. `=,…` |
| **Pre-Shared Key** | Yes | The PSK configured on your gateway for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route. Defaults to your database host `/32` |
**Checklist:**
* [ ] Your VPN gateway has one IKEv2 + pre-shared-key tunnel per Supaboard IP (the IPs shown under **Whitelist IPs**)
* [ ] The gateway allows UDP 500, UDP 4500, and ESP (IP protocol 50) from those IPs
* [ ] The **Host** field is the database's **private IP address** — private DNS names don't resolve from Supaboard
* [ ] Enter the database name manually — auto-discovery doesn't run through the VPN before the connection is saved
Gateway settings, supported ciphers, a strongSwan example, and troubleshooting: [IPsec Site-to-Site VPN guide](/data-sources/security/ipsec-vpn).
## Recommended database user permissions
```sql theme={null}
-- Create a read-only user
CREATE USER supaboard IDENTIFIED BY 'strong_password_here';
-- Grant SELECT on a specific database
GRANT SELECT ON your_database.* TO supaboard;
-- Optionally allow database discovery
GRANT SHOW DATABASES ON *.* TO supaboard;
GRANT SHOW TABLES ON your_database.* TO supaboard;
```
Replace `your_database` with the target database name. For multiple databases, repeat the `GRANT SELECT` line for each one.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `Connection refused` | Wrong host/port or firewall blocking | Verify host and port; add Supaboard IPs to the allow-list |
| `Authentication failed` | Wrong username or password | Verify credentials; confirm user exists with `SELECT name FROM system.users` |
| `Database X doesn't exist` | Database name typo | Check with `SHOW DATABASES` |
| `Code 516: supaboard: Authentication failed` | User exists but wrong password | Reset the user password: `ALTER USER supaboard IDENTIFIED BY 'new_password'` |
| Timeout on port 9000 | ClickHouse Cloud uses port 9440 | Update **Port** to `9440` for ClickHouse Cloud services |
# Databricks
Source: https://docs.supaboard.ai/data-sources/connectors/databricks
Connect Supaboard to a Databricks workspace using a personal access token and cluster HTTP path.
Open **Data sources → Connect Data Source → Databricks** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Databricks workspaces are accessible over HTTPS (port 443). No special firewall rules are typically needed for cloud-hosted workspaces. If your workspace is behind a private network or VPN, ensure Supaboard's egress IPs can reach it. The current IP list is shown inside the connector form under **Whitelist IPs**.
**Cluster requirements** — The cluster referenced by your HTTP path must be running when Supaboard connects. Terminated or auto-terminated clusters will cause connection failures.
**Permissions** — The user whose access token you use needs:
* `CAN ATTACH TO` permission on the cluster
* `SELECT` access on the target catalogs and schemas
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Token** | — | Yes | Databricks personal access token (PAT) |
| **Hostname** | — | Yes | Databricks workspace URL (without `https://`) |
| **Port** | `443` | Yes | HTTPS port — do not change |
| **HTTP Path** | — | Yes | Cluster or SQL warehouse JDBC/ODBC HTTP path |
| **Catalog** | `default` | No | Unity Catalog name; leave blank for the default catalog |
| **Schema** | `default` | No | Default schema for queries |
## Finding your connection details
### Databricks Workspace
**Personal access token (Token):**
1. In your Databricks workspace, click your username in the top-right corner.
2. Go to **Settings → Developer**.
3. Under **Access tokens**, click **Manage**.
4. Click **Generate new token**, add a comment (e.g. `supaboard`), set a lifetime, and click **Generate**.
5. Copy the token — it is only shown once.
**Documentation:** [Databricks personal access tokens](https://docs.databricks.com/en/dev-tools/auth/pat.html)
**Hostname:**
Your hostname is the workspace URL without `https://`. It looks like:
```text theme={null}
adb-1234567890123456.7.azuredatabricks.net
```
or for AWS:
```text theme={null}
dbc-xxxxxxxx-xxxx.cloud.databricks.com
```
Find it in your browser's address bar when logged into Databricks.
**HTTP Path:**
The HTTP Path identifies a specific cluster or SQL warehouse.
*For a cluster:*
1. Go to **Compute** in the sidebar.
2. Click on your cluster name.
3. Scroll to **Advanced options** and click the **JDBC/ODBC** tab.
4. Copy the **HTTP path** value (format: `/sql/1.0/endpoints/xxxxxxxxxxxxxxxx` or `/sql/protocolv1/o/...`).
*For a SQL warehouse:*
1. Go to **SQL Warehouses** in the sidebar.
2. Click on your warehouse.
3. Go to the **Connection details** tab.
4. Copy the **HTTP path**.
> SQL warehouses are recommended over interactive clusters for BI tools — they are purpose-built for query workloads and auto-scale automatically.
**Documentation:** [JDBC/ODBC connection details](https://docs.databricks.com/en/integrations/jdbc-odbc-bi.html) · [SQL warehouses overview](https://docs.databricks.com/en/compute/sql-warehouse/index.html)
## Recommended database user permissions
In Unity Catalog, grant the token owner the following privileges:
```sql theme={null}
-- Grant access to a specific catalog
GRANT USE CATALOG ON CATALOG your_catalog TO `user@example.com`;
-- Grant access to a specific schema
GRANT USE SCHEMA ON SCHEMA your_catalog.your_schema TO `user@example.com`;
-- Grant SELECT on tables
GRANT SELECT ON TABLE your_catalog.your_schema.your_table TO `user@example.com`;
-- Or grant SELECT on all tables in a schema
GRANT SELECT ON SCHEMA your_catalog.your_schema TO `user@example.com`;
```
For legacy (non-Unity Catalog) workspaces, ensure the user has the `data_access` group membership or equivalent.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `403 Forbidden` | Invalid or expired access token | Generate a new PAT in **Settings → Developer → Access tokens** |
| `Cluster not found` | Cluster terminated or HTTP path is wrong | Start the cluster; verify the HTTP path in **Compute → JDBC/ODBC** tab |
| `PERMISSION_DENIED` | User lacks access to catalog or schema | Grant `USE CATALOG`, `USE SCHEMA`, and `SELECT` privileges |
| Connection timeout | Workspace behind private network | Ensure Supaboard egress IPs can reach the workspace hostname on port 443 |
| `Invalid hostname` | Hostname includes `https://` | Remove the `https://` prefix from the **Hostname** field |
| Cluster auto-terminating | Cluster shuts down during schema discovery | Increase the auto-termination timeout or switch to a SQL warehouse |
# Fivetran
Source: https://docs.supaboard.ai/data-sources/connectors/fivetran
Supaboard integrates with **Fivetran** to bring data from 100+ SaaS tools, marketing platforms, and third-party services into your workspace — without writing or managing ETL pipelines.
Ten Fivetran connectors are included on every plan — Google Analytics, Typeform, Calendly, Stripe, Linear, Asana, YouTube Analytics, Notion, Auth0 and Airtable. Every other Fivetran connector needs the **Business** or **Enterprise** plan; selecting one on Individual shows the upgrade prompt.
## How it works
1. **Select a connector** — choose a Fivetran-powered service from the **Connect Data Source** picker.
2. **Authenticate** — Supaboard opens Fivetran's OAuth flow; you sign in to the source service to grant read access.
3. **Configure** — select the specific accounts, properties, or datasets to sync.
4. **Sync** — Fivetran begins syncing data on its schedule into a queryable form.
5. **Active** — once the first sync completes, the connection appears as **Active** in Supaboard and is available to agents and widgets.
> **Sync schedule vs real-time** — Fivetran syncs on a schedule (typically every hour to every 24 hours depending on the connector and plan). Data in Supaboard reflects the most recent completed sync, not a live stream. Check the connection's **Info** tab to see the last sync timestamp.
## Available connectors
### CRM
| Connector | Description |
| - | - |
| **Salesforce** | Leads, contacts, opportunities, accounts, and custom objects |
| **HubSpot** | CRM contacts, deals, companies, and marketing data |
| **Pipedrive** | Deals, contacts, and pipeline stages |
| **Zoho CRM** | Contacts, leads, deals, and modules |
| **Freshsales** | Contacts, accounts, deals, and activities |
| **Insightly** | Contacts, organizations, opportunities, and projects |
| **Attio** | Workspace records and relationship data |
| **Outreach** | Sequences, prospects, and engagement data |
| **Bigin by Zoho CRM** | Small-business pipeline and contact data |
### Marketing & Advertising
| Connector | Description |
| - | - |
| **Google Ads** | Campaigns, ad groups, keywords, and performance metrics |
| **Meta Ads** | Facebook and Instagram ad campaign performance |
| **LinkedIn Ads** | Campaign analytics and ad performance |
| **TikTok Ads** | Ad campaigns, creatives, and performance data |
| **Microsoft Ads** | Bing Ads campaign and keyword performance |
| **Amazon Ads** | Sponsored Products, Brands, and Display ad metrics |
| **Pinterest Ads** | Campaign performance and pin analytics |
| **Snapchat Ads** | Ad accounts, campaigns, and creative performance |
| **Reddit Ads** | Campaign and ad group performance data |
| **Apple Search Ads** | App Store ad campaign performance |
| **Twitter/X Ads** | Campaign performance and engagement metrics |
### Email & Communication
| Connector | Description |
| - | - |
| **Mailchimp** | Lists, campaigns, open/click rates, and e-commerce data |
| **Klaviyo** | Email flows, campaigns, and subscriber metrics |
| **Brevo** | Email campaigns, contacts, and transactional data |
| **SendGrid** | Email sends, bounces, clicks, and deliverability metrics |
| **Mailgun** | Email logs, events, and delivery data |
| **Postmark** | Transactional email delivery and bounce data |
| **Twilio** | SMS, voice, and messaging usage data |
| **ActiveCampaign** | Contacts, automations, campaigns, and deals |
| **Lemlist** | Outbound campaign sequences and engagement |
### Project Management
| Connector | Description |
| - | - |
| **Jira** | Issues, sprints, projects, and board data |
| **Asana** | Tasks, projects, teams, and workspaces |
| **Linear** | Issues, cycles, projects, and team metrics |
| **Monday.com** | Boards, items, and workflow data |
| **Notion** | Databases and page content |
| **ClickUp** | Tasks, spaces, folders, and time tracking |
| **Trello** | Boards, cards, and list data |
| **Airtable** | Bases, tables, and records |
| **Confluence** | Pages, spaces, and content metadata |
| **Coda** | Docs, tables, and formula data |
| **Wrike** | Projects, tasks, and time tracking |
| **Productboard** | Features, objectives, and feedback data |
### Customer Support
| Connector | Description |
| - | - |
| **Zendesk** | Tickets, agents, satisfaction scores, and SLA data |
| **Freshdesk** | Tickets, agents, contacts, and CSAT data |
| **Intercom** | Conversations, contacts, companies, and events |
| **ServiceNow** | Incidents, requests, and ITSM workflow data |
| **Zoho Desk** | Tickets, contacts, and support analytics |
### Analytics & Product
| Connector | Description |
| - | - |
| **Google Analytics 4** | Sessions, events, conversions, and user properties |
| **Mixpanel** | Events, funnels, retention, and user profiles |
| **Amplitude** | Events, user journeys, and behavioural analytics |
| **Heap** | Auto-captured events and user sessions |
| **PostHog** | Product events, sessions, and feature flags |
| **Segment** | Track, identify, and group events |
| **Fullstory** | Session replay metadata and frustration signals |
| **Datadog** | Infrastructure metrics, logs, and APM data |
| **Sentry** | Errors, performance issues, and release data |
| **YouTube Analytics** | Channel performance, video metrics, and audience data |
### E-commerce & Payments
| Connector | Description |
| - | - |
| **Shopify** | Orders, products, customers, and inventory |
| **WooCommerce** | Orders, customers, and product data |
| **BigCommerce** | Orders, products, and customer data |
| **Stripe** | Charges, subscriptions, invoices, and payouts |
| **PayPal** | Transactions, invoices, and dispute data |
| **Chargebee** | Subscriptions, invoices, and revenue metrics |
| **QuickBooks** | Accounting data, invoices, expenses, and P\&L |
| **Paddle** | Subscriptions, transactions, and billing data |
| **Razorpay** | Payments, orders, and refund data |
### HR & People
| Connector | Description |
| - | - |
| **BambooHR** | Employee profiles, time-off, and org structure |
| **Rippling** | HR, payroll, and IT data |
| **Workday** | HCM, payroll, and financial data |
### Forms & Surveys
| Connector | Description |
| - | - |
| **Typeform** | Form responses and completion metrics |
| **SurveyMonkey** | Survey results and response data |
| **JotForm** | Form submissions and response analytics |
### Communication & Scheduling
| Connector | Description |
| - | - |
| **Slack** | Messages, channels, and workspace analytics |
| **Microsoft Teams** | Messages, channels, and meeting data |
| **Calendly** | Scheduled events, invitees, and routing data |
### Social & Content
| Connector | Description |
| - | - |
| **Instagram Business** | Posts, stories, follower metrics, and engagement |
| **Vimeo** | Video performance and view analytics |
| **WordPress** | Posts, pages, comments, and user data |
## Plan availability
| Plan | Fivetran connectors |
| - | - |
| **Individual** | The 10 included connectors listed above |
| **Business** | All connectors |
| **Enterprise** | All connectors |
## Fivetran documentation
Each connector has detailed setup instructions, required permissions, and field references in the Fivetran documentation.
* [Full connector catalogue](https://fivetran.com/docs/connectors)
* Individual connector pages follow the pattern `https://fivetran.com/docs/connectors/{service}` — for example, [Salesforce](https://fivetran.com/docs/connectors/salesforce), [HubSpot](https://fivetran.com/docs/connectors/hubspot), [Shopify](https://fivetran.com/docs/connectors/shopify), [Google Ads](https://fivetran.com/docs/connectors/google-ads), [Stripe](https://fivetran.com/docs/connectors/stripe).
* [Fivetran account setup and billing](https://fivetran.com/docs/getting-started)
* [Fivetran data security and privacy](https://fivetran.com/docs/security)
# Microsoft SQL Server
Source: https://docs.supaboard.ai/data-sources/connectors/microsoft-sql-server
Connect Supaboard to a SQL Server instance — whether on Azure, AWS, GCP, or self-hosted — for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → MS SQL server** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supaboard connects from a fixed set of egress IPs. Add them to your SQL Server firewall allow-list before attempting a connection. The current IP list is shown inside the connector form under **Whitelist IPs**.
**Database user permissions** — The user you provide needs at minimum:
* `CONNECT` on the database
* `SELECT` on the target schema(s) and tables
See [Recommended database user permissions](#recommended-database-user-permissions) for the exact SQL.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | — | Yes | Hostname or IP of your SQL Server instance |
| **Port** | `3342` | Yes | SQL Server port (see note below) |
| **Database** | `master` | No | Database name; Supaboard auto-discovers if left blank |
| **Schema** | `dbo` | No | Default schema for queries |
| **Username** | — | Yes | SQL Server login |
| **Password** | — | Yes | Password for the SQL Server login |
| **Additional JDBC Parameters** | — | No | Extra key=value pairs appended to the JDBC URL |
> **Port note** — The standard on-premises SQL Server port is `1433`. Azure SQL Database and many managed SQL Server instances use `1433` as well. Supaboard's default shows `3342` — update it to `1433` for Azure SQL or most RDS/Cloud SQL instances.
## SSH Tunnel fields
Shown when the **SSH Tunnel** toggle is enabled.
| Field | Default | Required | Description |
| - | - | - | - |
| **SSH Host** | — | Yes | Hostname or IP of the SSH bastion server |
| **SSH Port** | `22` | Yes | SSH port on the bastion server |
| **SSH Username** | — | Yes | SSH login username |
| **SSH Connection Type** | `Private Key` | Yes | `Private Key` or `Password` |
| **SSH Private Key** | — | If Private Key | PEM-encoded private key |
| **SSH Passphrase** | — | No | Passphrase for an encrypted private key |
| **SSH Password** | — | If Password | Password for SSH password authentication |
## Finding your connection details
### Azure SQL
1. Open your SQL server in the [Azure Portal](https://portal.azure.com).
2. On the **Overview** page, copy **Server name** — this is your **Host** (format: `servername.database.windows.net`).
3. The default **Port** is `1433`.
4. Under **Security → Networking**, add a firewall rule for each Supaboard egress IP.
5. Ensure **Allow Azure services and resources to access this server** is set if needed.
Your **Database** name appears on the **SQL databases** page under the server.
**Documentation:** [Azure SQL Database documentation](https://learn.microsoft.com/en-us/azure/azure-sql/database/) · [Configure firewall rules](https://learn.microsoft.com/en-us/azure/azure-sql/database/firewall-configure)
### Amazon RDS for SQL Server
1. Open the [RDS Console](https://console.aws.amazon.com/rds/) and select your instance.
2. Under **Connectivity & security**, copy the **Endpoint** — this is your **Host**.
3. The default **Port** is `1433`.
4. Add Supaboard egress IPs to the instance's VPC security group (inbound TCP on port 1433).
**Documentation:** [Connecting to a SQL Server DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ConnectToMicrosoftSQLServerInstance.html) · [RDS for SQL Server overview](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_SQLServer.html)
### Google Cloud SQL for SQL Server
1. Open the [Cloud SQL Console](https://console.cloud.google.com/sql) and select your instance.
2. Under **Connect to this instance**, copy the **Public IP address** — this is your **Host**.
3. Default **Port** is `1433`.
4. Under **Connections → Authorized networks**, add each Supaboard egress IP.
**Documentation:** [Cloud SQL for SQL Server overview](https://cloud.google.com/sql/docs/sqlserver) · [Authorizing with IP addresses](https://cloud.google.com/sql/docs/sqlserver/authorize-networks)
## SSL
Enable the **SSL** toggle when your database requires encrypted connections (recommended for Azure SQL and all cloud-hosted instances — Azure SQL enforces SSL by default).
* **SSL Certificate** — paste the server's CA certificate in PEM format if your instance uses a self-signed or private CA. For Azure SQL and AWS RDS, the certificates are publicly trusted and no upload is needed.
## SSH Tunnel
Use an SSH tunnel when your SQL Server instance is in a private network with no public IP.
**When to use it:**
* Instance is not publicly accessible
* On-premises SQL Server behind a corporate firewall
* Security policy requires all external traffic to pass through a bastion host
**Checklist:**
* [ ] The bastion server can reach the SQL Server host on port 1433
* [ ] Supaboard's egress IPs are allowed on the bastion server's SSH port (default 22)
* [ ] The SSH user has permission to forward connections
* [ ] If using a private key, it is in PEM format
## IPsec Site-to-Site VPN
If your database lives on a private network, you can peer your VPN gateway with Supaboard over IKEv2 IPsec instead of exposing the database or running a bastion host. Enable the **Use IPsec site-to-site VPN** toggle in the connector form.
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway — or a per-server mapping for providers with one tunnel endpoint per peer (OCI/AWS managed VPN), e.g. `=,…` |
| **Pre-Shared Key** | Yes | The PSK configured on your gateway for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route. Defaults to your database host `/32` |
**Checklist:**
* [ ] Your VPN gateway has one IKEv2 + pre-shared-key tunnel per Supaboard IP (the IPs shown under **Whitelist IPs**)
* [ ] The gateway allows UDP 500, UDP 4500, and ESP (IP protocol 50) from those IPs
* [ ] The **Host** field is the database's **private IP address** — private DNS names don't resolve from Supaboard
* [ ] Enter the database name manually — auto-discovery doesn't run through the VPN before the connection is saved
Gateway settings, supported ciphers, a strongSwan example, and troubleshooting: [IPsec Site-to-Site VPN guide](/data-sources/security/ipsec-vpn).
## Recommended database user permissions
```sql theme={null}
-- Create a SQL Server login
CREATE LOGIN supaboard WITH PASSWORD = 'Strong_Password_Here!';
-- Create a database user mapped to the login
USE your_database;
CREATE USER supaboard FOR LOGIN supaboard;
-- Grant read-only access to the default schema
GRANT SELECT ON SCHEMA::dbo TO supaboard;
-- Grant connect and select on the database
GRANT CONNECT TO supaboard;
```
For multiple schemas, repeat the `GRANT SELECT ON SCHEMA` line for each schema you want to expose. For finer-grained control, grant SELECT on individual tables:
```sql theme={null}
GRANT SELECT ON dbo.your_table TO supaboard;
```
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `Login failed for user` | Wrong username or password | Verify SQL Server login credentials |
| `Cannot open server requested by the login` | Wrong database name or user lacks access | Confirm database name and that the user has CONNECT grant |
| `Connection refused` or timeout | Firewall blocking Supaboard IPs, or wrong port | Add Supaboard IPs to the firewall; verify port (1433 for most cloud instances) |
| `SSL required` | Server requires SSL but toggle is off | Enable the SSL toggle |
| `Named Pipes Provider: Could not open connection` | Named Pipes enabled instead of TCP | Ensure TCP/IP is enabled in SQL Server Configuration Manager |
| `User does not have permission to perform this action` | Insufficient schema/table grants | Run the `GRANT SELECT ON SCHEMA` statements above |
# MongoDB
Source: https://docs.supaboard.ai/data-sources/connectors/mongodb
Connect Supaboard to a MongoDB database using a standard connection string (URI).
Open **Data sources → Connect Data Source → MongoDB** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supaboard connects from a fixed set of egress IPs. For MongoDB Atlas, add them to your cluster's IP access list under **Security → Network Access**. The current IP list is shown inside the connector form under **Whitelist IPs**.
**Database user permissions** — The user in your connection string needs at minimum:
* `read` role on the target database
See [Recommended database user permissions](#recommended-database-user-permissions) for instructions.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Connection String** | — | Yes | Full MongoDB URI (see format below) |
## Connection string format
Supaboard accepts a full MongoDB URI. The standard format is:
```text theme={null}
mongodb+srv://username:password@cluster-host/?authSource=admin
```
For non-SRV connections (self-hosted or older Atlas clusters):
```text theme={null}
mongodb://username:password@host:27017/database?authSource=admin
```
| Part | Description |
| - | - |
| `mongodb+srv://` | Use `+srv` for Atlas and DNS-based clusters; use `mongodb://` for direct host connections |
| `username:password` | MongoDB user credentials — URL-encode special characters (e.g. `@` → `%40`) |
| `cluster-host` | Cluster hostname (Atlas) or server IP/hostname (self-hosted) |
| `database` | Optional — the specific database to connect to; if omitted, Supaboard auto-discovers |
| `authSource=admin` | The authentication database — usually `admin` for Atlas |
## Finding your connection details
### MongoDB Atlas
1. Open [cloud.mongodb.com](https://cloud.mongodb.com) and select your project.
2. In the **Clusters** view, click **Connect** on your cluster.
3. Select **Drivers** (or **Connect your application**).
4. Choose any driver — you only need the connection string, not the code snippet.
5. Copy the connection string. It will look like:
```text theme={null}
mongodb+srv://:@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority
```
6. Replace `` and `` with your Atlas database user credentials.
7. Paste the full string into the **Connection String** field in Supaboard.
**Documentation:** [Connect to a cluster](https://www.mongodb.com/docs/atlas/connect-to-database-deployment/) · [Connection string URI format](https://www.mongodb.com/docs/manual/reference/connection-string/)
**Adding Supaboard IPs to Atlas:**
Atlas requires you to explicitly allow external IPs before any connection can succeed.
1. In your Atlas project, go to **Security → Network Access**.
2. Click **Add IP Address**.
3. Add each Supaboard egress IP individually (found in the connector form under **Whitelist IPs**).
4. Click **Confirm**.
> Atlas IP access list changes take effect immediately. You do not need to restart your cluster.
**Documentation:** [Configure IP access list entries](https://www.mongodb.com/docs/atlas/security/ip-access-list/)
## IPsec Site-to-Site VPN
If your database lives on a private network, you can peer your VPN gateway with Supaboard over IKEv2 IPsec instead of exposing the database or running a bastion host. Enable the **Use IPsec site-to-site VPN** toggle in the connector form.
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway — or a per-server mapping for providers with one tunnel endpoint per peer (OCI/AWS managed VPN), e.g. `=,…` |
| **Pre-Shared Key** | Yes | The PSK configured on your gateway for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route. Defaults to your database host `/32` |
**Checklist:**
* [ ] Your VPN gateway has one IKEv2 + pre-shared-key tunnel per Supaboard IP (the IPs shown under **Whitelist IPs**)
* [ ] The gateway allows UDP 500, UDP 4500, and ESP (IP protocol 50) from those IPs
* [ ] The **Host** field is the database's **private IP address** — private DNS names don't resolve from Supaboard
* [ ] Enter the database name manually — auto-discovery doesn't run through the VPN before the connection is saved
Gateway settings, supported ciphers, a strongSwan example, and troubleshooting: [IPsec Site-to-Site VPN guide](/data-sources/security/ipsec-vpn).
## Recommended database user permissions
In MongoDB Atlas:
1. Go to **Security → Database Access**.
2. Click **Add New Database User**.
3. Choose **Password** authentication.
4. Set a username and strong password.
5. Under **Database User Privileges**, select **Built-in Role → Read Only** (or use a custom role scoped to specific databases).
6. Click **Add User**.
7. Use this username and password in your connection string.
For self-hosted MongoDB:
```javascript theme={null}
use your_database
db.createUser({
user: "supaboard",
pwd: "strong_password_here",
roles: [{ role: "read", db: "your_database" }]
})
```
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `connection refused` | Atlas IP access list missing Supaboard IPs | Add all Supaboard egress IPs under **Security → Network Access** |
| `Authentication failed` | Wrong username or password in the URI | Verify credentials in Atlas under **Security → Database Access** |
| `bad auth : authentication failed` | Wrong `authSource` | Add `?authSource=admin` to the URI |
| `URI must include hostname` | Malformed connection string | Check that the URI starts with `mongodb://` or `mongodb+srv://` |
| `querySrv ENODATA` | DNS resolution failing for `+srv` URI | Try switching to a direct `mongodb://` URI with explicit host and port |
| Special characters in password | URI parsing error | URL-encode special characters: `@` → `%40`, `#` → `%23`, `:` → `%3A` |
# MySQL
Source: https://docs.supaboard.ai/data-sources/connectors/mysql
Connect Supaboard to a MySQL database for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → MySql** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supaboard connects from a fixed set of egress IPs. Add them to your database firewall allow-list before attempting a connection. The current IP list is shown inside the connector form under **Whitelist IPs**.
**Database user permissions** — The user you provide needs at minimum:
* `SELECT` on the target database
* `SHOW DATABASES` to enable database auto-discovery
See [Recommended database user permissions](#recommended-database-user-permissions) for the exact SQL.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | — | Yes | Hostname or IP of your MySQL server |
| **Port** | `3306` | Yes | MySQL port |
| **Database** | `mysql` | No | Database name; Supaboard auto-discovers if left blank |
| **Username** | — | Yes | Database user |
| **Password** | — | Yes | Password for the database user |
| **Additional JDBC Parameters** | — | No | Extra key=value pairs appended to the JDBC URL |
> **Boolean columns** — MySQL's `TINYINT(1)` type is interpreted as a boolean by many JDBC drivers. If your queries return `true`/`false` instead of `0`/`1` unexpectedly, add `tinyInt1isBit=false` to **Additional JDBC Parameters**.
## SSH Tunnel fields
Shown when the **SSH Tunnel** toggle is enabled.
| Field | Default | Required | Description |
| - | - | - | - |
| **SSH Host** | — | Yes | Hostname or IP of the SSH bastion server |
| **SSH Port** | `22` | Yes | SSH port on the bastion server |
| **SSH Username** | — | Yes | SSH login username |
| **SSH Connection Type** | `Private Key` | Yes | `Private Key` or `Password` |
| **SSH Private Key** | — | If Private Key | PEM-encoded private key |
| **SSH Passphrase** | — | No | Passphrase for an encrypted private key |
| **SSH Password** | — | If Password | Password for SSH password authentication |
## Finding your connection details
### Amazon RDS for MySQL
1. Open the [RDS Console](https://console.aws.amazon.com/rds/) and select your DB instance.
2. Under **Connectivity & security**, copy the **Endpoint** — this is your **Host**.
3. The default **Port** is `3306`.
4. Ensure the instance's security group allows inbound TCP on port 3306 from Supaboard's egress IPs, or route through an SSH tunnel.
**Documentation:** [Connecting to a MySQL DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ConnectToInstance.html) · [RDS for MySQL overview](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_MySQL.html)
### Amazon Aurora (MySQL-compatible)
1. In the RDS Console, open your Aurora cluster.
2. Use the **Writer endpoint** as your **Host** for read/write access; use a **Reader endpoint** for read-only.
3. Default port is `3306`.
4. Add Supaboard IPs to the cluster's VPC security group.
**Documentation:** [Connecting to an Aurora MySQL cluster](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html) · [Aurora MySQL overview](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraMySQL.html)
### Google Cloud SQL for MySQL
1. Open the [Cloud SQL Console](https://console.cloud.google.com/sql) and select your instance.
2. Under **Connect to this instance**, copy the **Public IP address** as your **Host**.
3. Under **Connections → Authorized networks**, add each Supaboard egress IP.
4. Alternatively, use the Cloud SQL Auth Proxy via an SSH tunnel.
**Documentation:** [Cloud SQL for MySQL overview](https://cloud.google.com/sql/docs/mysql) · [Connecting overview](https://cloud.google.com/sql/docs/mysql/connect-overview) · [Authorizing with IP addresses](https://cloud.google.com/sql/docs/mysql/authorize-networks)
### Azure Database for MySQL
1. Open your server in the [Azure Portal](https://portal.azure.com).
2. On the **Overview** page, copy **Server name** — this is your **Host** (format: `server.mysql.database.azure.com`).
3. Under **Settings → Connection security**, add a firewall rule for each Supaboard egress IP.
4. SSL enforcement is enabled by default on Azure — enable the SSL toggle in Supaboard.
**Documentation:** [Azure Database for MySQL documentation](https://learn.microsoft.com/en-us/azure/mysql/) · [Firewall rules](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-firewall-rules)
## SSL
Enable the **SSL** toggle when your database requires encrypted connections (recommended for all cloud-hosted instances).
* **SSL Certificate** — paste the server's CA certificate in PEM format if your database uses a self-signed or private CA certificate. Leave blank for certificates signed by a public CA.
## SSH Tunnel
An SSH tunnel routes the database connection through a bastion host, keeping your database off the public internet entirely.
**When to use it:**
* Your database has no public IP
* You want to avoid adding Supaboard IPs to your database firewall
* Your security policy requires all external connections to go through a jump server
**Checklist:**
* [ ] The bastion server can reach the database host on port 3306
* [ ] Supaboard's egress IPs are allowed on the bastion server's SSH port (default 22)
* [ ] The SSH user has permission to forward connections (no `no-port-forwarding` in `authorized_keys`)
* [ ] If using a private key, it is in PEM format (OpenSSH format may need conversion with `ssh-keygen -p -m PEM`)
## IPsec Site-to-Site VPN
If your database lives on a private network, you can peer your VPN gateway with Supaboard over IKEv2 IPsec instead of exposing the database or running a bastion host. Enable the **Use IPsec site-to-site VPN** toggle in the connector form.
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway — or a per-server mapping for providers with one tunnel endpoint per peer (OCI/AWS managed VPN), e.g. `=,…` |
| **Pre-Shared Key** | Yes | The PSK configured on your gateway for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route. Defaults to your database host `/32` |
**Checklist:**
* [ ] Your VPN gateway has one IKEv2 + pre-shared-key tunnel per Supaboard IP (the IPs shown under **Whitelist IPs**)
* [ ] The gateway allows UDP 500, UDP 4500, and ESP (IP protocol 50) from those IPs
* [ ] The **Host** field is the database's **private IP address** — private DNS names don't resolve from Supaboard
* [ ] Enter the database name manually — auto-discovery doesn't run through the VPN before the connection is saved
Gateway settings, supported ciphers, a strongSwan example, and troubleshooting: [IPsec Site-to-Site VPN guide](/data-sources/security/ipsec-vpn).
## Recommended database user permissions
```sql theme={null}
-- Create a dedicated read-only user (accessible from any host)
CREATE USER 'supaboard'@'%' IDENTIFIED BY 'strong_password_here';
-- Grant SELECT on the target database
GRANT SELECT ON your_database.* TO 'supaboard'@'%';
-- Allow database discovery
GRANT SHOW DATABASES ON *.* TO 'supaboard'@'%';
FLUSH PRIVILEGES;
```
Replace `your_database` with the name of your database. To expose multiple databases, repeat the `GRANT SELECT` line for each one.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `Access denied for user` | Wrong username or password | Verify credentials; confirm the user exists with `SELECT user FROM mysql.user` |
| `Can't connect to MySQL server` | Wrong host/port or firewall blocking | Check host/port; add Supaboard IPs to firewall allow-list |
| `Unknown database` | Database name doesn't exist | Verify the database name with `SHOW DATABASES` |
| `SSL connection required` | Server requires SSL but toggle is off | Enable the SSL toggle |
| `Host is not allowed to connect` | MySQL's `bind-address` or user host restriction | Ensure the user is created with `'%'` host, not `'localhost'` |
| `tinyInt1isBit` confusion | TINYINT(1) returned as boolean | Add `tinyInt1isBit=false` to Additional JDBC Parameters |
# Oracle
Source: https://docs.supaboard.ai/data-sources/connectors/oracle
Connect Supaboard to an Oracle database for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → Oracle SQL** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supaboard connects from a fixed set of egress IPs. Add them to your database firewall allow-list before attempting a connection. The current IP list is shown inside the connector form under **Whitelist IPs**.
**Database user permissions** — The user you provide needs at minimum:
* `CREATE SESSION` to connect
* `SELECT` on the tables and views you want to expose
See [Recommended database user permissions](#recommended-database-user-permissions) for the exact SQL.
**Service name vs. SID** — The form connects by **service name**. If your database is only reachable by SID, leave **Service Name** empty and put `sid=YOURSID` in **Additional JDBC Parameters**.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | — | Yes | Hostname or IP of your Oracle server |
| **Port** | `1521` | Yes | Oracle listener port (Autonomous Database typically uses `1522`) |
| **Service Name** | — | Yes | The database service name, e.g. `FREEPDB1` (use `sid=` in Additional JDBC Parameters instead for SID-only setups) |
| **Username** | — | Yes | Database user |
| **Password** | — | Yes | Password for the database user |
| **Schema** | uppercase of username | No | Schema whose tables are exposed; defaults to the user's own schema |
| **Additional JDBC Parameters** | — | No | Extra `key=value` pairs passed to the driver, e.g. `sid=XE` or `wallet=/path` |
## SSH Tunnel fields
Shown when the **SSH Tunnel** toggle is enabled.
| Field | Default | Required | Description |
| - | - | - | - |
| **SSH Host** | — | Yes | Hostname or IP of the SSH bastion server |
| **SSH Port** | `22` | Yes | SSH port on the bastion server |
| **SSH Username** | — | Yes | SSH login username |
| **SSH Connection Type** | `Private Key` | Yes | `Private Key` or `Password` |
| **SSH Private Key** | — | If Private Key | PEM-encoded private key |
| **SSH Passphrase** | — | No | Passphrase for an encrypted private key |
| **SSH Password** | — | If Password | Password for SSH password authentication |
## Finding your connection details
### Oracle Autonomous Database (OCI)
1. In the [OCI Console](https://cloud.oracle.com/), open your Autonomous Database and click **Database connection**.
2. Pick a TNS name (e.g. `yourdb_low`) and read the host, port, and service name from its connection string — the host looks like `adb..oraclecloud.com`, the port is usually `1522`.
3. Enable the **SSL** toggle in Supaboard — Autonomous Database requires TLS.
4. Under **Network → Access control list**, allow each Supaboard egress IP (or set the database to allow secure access from anywhere).
**Documentation:** [Connect to Autonomous Database](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/connect-intro.html) · [TLS connections without a wallet](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/connecting-nodejs-tls.html)
### Amazon RDS for Oracle
1. Open the [RDS Console](https://console.aws.amazon.com/rds/) and select your instance.
2. Under **Connectivity & security**, copy the **Endpoint** — this is your **Host**. The default **Port** is `1521`.
3. The **Service Name** is the **DB name** from the **Configuration** tab (RDS creates the database with a service of the same name).
4. Allow inbound TCP on port 1521 from Supaboard's egress IPs in the instance's security group, or use an SSH tunnel.
**Documentation:** [Connecting to an RDS for Oracle instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ConnectToOracleInstance.html)
### Self-hosted Oracle
1. **Host/Port** — the machine running the listener; check with `lsnrctl status` on the server.
2. **Service Name** — listed in the `Services Summary` of `lsnrctl status` (for Oracle Database Free the default PDB service is `FREEPDB1`; for XE it's `XEPDB1`).
3. If only a SID is registered, use `sid=` in **Additional JDBC Parameters**.
## SSL
Enable the **SSL** toggle when your database requires TLS (always on for OCI Autonomous Database). This uses the driver's native TLS — no wallet file is needed for Autonomous Database when TLS access is enabled on the OCI side.
## SSH Tunnel
An SSH tunnel routes the database connection through a bastion host, keeping your database off the public internet entirely.
**When to use it:**
* Your database has no public IP
* You want to avoid adding Supaboard IPs to your database firewall
* Your security policy requires all external connections to go through a jump server
**Checklist:**
* [ ] The bastion server can reach the database host on port 1521
* [ ] Supaboard's egress IPs are allowed on the bastion server's SSH port (default 22)
* [ ] The SSH user has permission to forward connections (no `no-port-forwarding` in `authorized_keys`)
* [ ] If using a private key, it is in PEM format (OpenSSH format may need conversion with `ssh-keygen -p -m PEM`)
## IPsec Site-to-Site VPN
If your database lives on a private network, you can peer your VPN gateway with Supaboard over IKEv2 IPsec instead of exposing the database or running a bastion host. Enable the **Use IPsec site-to-site VPN** toggle in the connector form.
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway — or a per-server mapping for providers with one tunnel endpoint per peer (OCI/AWS managed VPN), e.g. `=,…` |
| **Pre-Shared Key** | Yes | The PSK configured on your gateway for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route. Defaults to your database host `/32` |
**Checklist:**
* [ ] Your VPN gateway has one IKEv2 + pre-shared-key tunnel per Supaboard IP (the IPs shown under **Whitelist IPs**)
* [ ] The gateway allows UDP 500, UDP 4500, and ESP (IP protocol 50) from those IPs
* [ ] The **Host** field is the database's **private IP address** — private DNS names don't resolve from Supaboard
* [ ] Enter the database name manually — auto-discovery doesn't run through the VPN before the connection is saved
Gateway settings, supported ciphers, a strongSwan example, and troubleshooting: [IPsec Site-to-Site VPN guide](/data-sources/security/ipsec-vpn).
## Recommended database user permissions
```sql theme={null}
-- Create a dedicated read-only user
CREATE USER supaboard IDENTIFIED BY "strong_password_here";
-- Allow connection
GRANT CREATE SESSION TO supaboard;
-- Allow reading the tables you want to expose (repeat per table)
GRANT SELECT ON app_schema.orders TO supaboard;
GRANT SELECT ON app_schema.customers TO supaboard;
```
To expose another schema's tables, set the **Schema** field to that schema's name (e.g. `APP_SCHEMA`) — by default Supaboard reads the connecting user's own schema. Avoid `GRANT SELECT ANY TABLE` unless your security policy allows it; per-table grants keep the blast radius small.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `ORA-01017: invalid username/password` | Wrong credentials, or a case-sensitive quoted username | Verify credentials; unquoted Oracle usernames are stored uppercase |
| `ORA-12514: listener does not currently know of service` | Wrong service name | Check `lsnrctl status` on the server; for SID-only databases use `sid=` in Additional JDBC Parameters |
| `ORA-12541: no listener` | Wrong host/port, or listener down | Verify host and port; confirm the listener is running |
| `ORA-12170: connect timeout` | Firewall blocking the connection | Add Supaboard's egress IPs to the firewall, or use an SSH tunnel / IPsec VPN |
| TLS/handshake errors on Autonomous Database | SSL toggle off, or ACL blocking | Enable the **SSL** toggle and allow Supaboard IPs in the OCI access control list |
| `ORA-00942: table or view does not exist` | Missing `SELECT` grant, or wrong schema | Grant `SELECT` on the table; set the **Schema** field to the owning schema |
# Postgres
Source: https://docs.supaboard.ai/data-sources/connectors/postgres
Connect Supaboard to a PostgreSQL database for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → PostgreSQL** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supaboard connects from a fixed set of egress IPs. Add them to your database firewall allow-list before attempting a connection. The current IP list is shown inside the connector form under **Allowlist Supaboard IP Addresses**.
**Database user permissions** — The user you provide needs at minimum:
* `CONNECT` on the database
* `USAGE` on the target schema(s)
* `SELECT` on the tables you want to expose
See [Recommended database user permissions](#recommended-database-user-permissions) for the exact SQL.
## Connect PostgreSQL
1. Open **Data sources → Connect Data Source → PostgreSQL**.
2. Enter a recognizable **Connection Name**.
3. Paste a **Connection URL**, or fill in the host, port, username, password, database, and schema fields below.
4. Configure SSL and the network connection method your database requires. Use the current IP list shown in the form for your firewall.
5. Choose **Connect PostgreSQL**. Wait for table discovery, then open the source and confirm the intended tables are available.
Once connected, make the source available to the agent you will use and ask a simple question to check the result.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Connection Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | — | Yes | Hostname or IP of your PostgreSQL server |
| **Port** | `5432` | Yes | PostgreSQL port |
| **Database** | `postgres` | No | Database name; Supaboard auto-discovers if left blank |
| **Username** | — | Yes | Database user |
| **Password** | — | Yes | Password for the database user |
| **Schema** | `public` | No | Default schema for queries |
| **Additional JDBC Parameters** | — | No | Extra key=value pairs appended to the JDBC URL |
## SSH Tunnel fields
Shown when the **SSH Tunnel** toggle is enabled.
| Field | Default | Required | Description |
| - | - | - | - |
| **SSH Host** | — | Yes | Hostname or IP of the SSH bastion server |
| **SSH Port** | `22` | Yes | SSH port on the bastion server |
| **SSH Username** | — | Yes | SSH login username |
| **SSH Connection Type** | `Private Key` | Yes | `Private Key` or `Password` |
| **SSH Private Key** | — | If Private Key | PEM-encoded private key |
| **SSH Passphrase** | — | No | Passphrase for an encrypted private key |
| **SSH Password** | — | If Password | Password for SSH password authentication |
## Finding your connection details
### Amazon RDS for PostgreSQL
1. Open the [RDS Console](https://console.aws.amazon.com/rds/) and select your instance.
2. Under **Connectivity & security**, copy the **Endpoint** — this is your **Host**.
3. The default **Port** is `5432`.
4. Your **Database** name was set when the instance was created (visible in the **Configuration** tab).
5. Ensure the RDS security group allows inbound TCP on port 5432 from Supaboard's egress IPs, or use an SSH tunnel.
**Documentation:** [Connecting to a PostgreSQL DB instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ConnectToPostgreSQLInstance.html) · [RDS for PostgreSQL overview](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html)
### Amazon Aurora (PostgreSQL-compatible)
1. In the RDS Console, open your Aurora cluster.
2. Use the **Writer endpoint** as your **Host** for read/write access; use a **Reader endpoint** for read-only.
3. Port is `5432` by default.
4. Add Supaboard IPs to the cluster's VPC security group.
**Documentation:** [Connecting to an Aurora PostgreSQL cluster](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.Connecting.html) · [Aurora PostgreSQL overview](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraPostgreSQL.html)
### Google Cloud SQL for PostgreSQL
1. Open the [Cloud SQL Console](https://console.cloud.google.com/sql) and select your instance.
2. Under **Connect to this instance**, copy the **Public IP address** as your **Host**.
3. Under **Connections → Authorized networks**, add each Supaboard egress IP.
4. Alternatively, use the Cloud SQL Auth Proxy via an SSH tunnel.
**Documentation:** [Cloud SQL for PostgreSQL overview](https://cloud.google.com/sql/docs/postgres) · [Connecting overview](https://cloud.google.com/sql/docs/postgres/connect-overview) · [Authorizing with IP addresses](https://cloud.google.com/sql/docs/postgres/authorize-networks)
### Azure Database for PostgreSQL
1. Open your server in the [Azure Portal](https://portal.azure.com).
2. On the **Overview** page, copy **Server name** — this is your **Host** (format: `server.postgres.database.azure.com`).
3. Under **Settings → Connection security**, add a firewall rule for each Supaboard egress IP.
4. SSL enforcement is enabled by default on Azure — enable the SSL toggle in Supaboard.
**Documentation:** [Azure Database for PostgreSQL documentation](https://learn.microsoft.com/en-us/azure/postgresql/) · [Firewall rules](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-firewall-rules)
## SSL
Enable the **SSL** toggle when your database requires encrypted connections (recommended for all cloud-hosted instances).
* **SSL Certificate** — paste the server's CA certificate in PEM format if your database uses a self-signed or private CA certificate. Leave blank for certificates signed by a public CA.
## SSH Tunnel
An SSH tunnel routes the database connection through a bastion host, keeping your database off the public internet entirely.
**When to use it:**
* Your database has no public IP
* You want to avoid adding Supaboard IPs to your database firewall
* Your security policy requires all external connections to go through a jump server
**Checklist:**
* [ ] The bastion server can reach the database host on port 5432
* [ ] Supaboard's egress IPs are allowed on the bastion server's SSH port (default 22)
* [ ] The SSH user has permission to forward connections (no `no-port-forwarding` in `authorized_keys`)
* [ ] If using a private key, it is in PEM format (OpenSSH format may need conversion with `ssh-keygen -p -m PEM`)
## IPsec Site-to-Site VPN
If your database lives on a private network, you can peer your VPN gateway with Supaboard over IKEv2 IPsec instead of exposing the database or running a bastion host. Enable the **Use IPsec site-to-site VPN** toggle in the connector form.
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway — or a per-server mapping for providers with one tunnel endpoint per peer (OCI/AWS managed VPN), e.g. `=,…` |
| **Pre-Shared Key** | Yes | The PSK configured on your gateway for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route. Defaults to your database host `/32` |
**Checklist:**
* [ ] Your VPN gateway has one IKEv2 + pre-shared-key tunnel per Supaboard IP (the IPs shown under **Whitelist IPs**)
* [ ] The gateway allows UDP 500, UDP 4500, and ESP (IP protocol 50) from those IPs
* [ ] The **Host** field is the database's **private IP address** — private DNS names don't resolve from Supaboard
* [ ] Enter the database name manually — auto-discovery doesn't run through the VPN before the connection is saved
Gateway settings, supported ciphers, a strongSwan example, and troubleshooting: [IPsec Site-to-Site VPN guide](/data-sources/security/ipsec-vpn).
## Recommended database user permissions
```sql theme={null}
-- Create a dedicated read-only user
CREATE USER supaboard WITH PASSWORD 'strong_password_here';
-- Allow connection to the database
GRANT CONNECT ON DATABASE your_database TO supaboard;
-- Allow schema access
GRANT USAGE ON SCHEMA public TO supaboard;
-- Allow reading all current tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO supaboard;
-- Allow reading future tables automatically
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO supaboard;
```
Replace `public` with each schema you want to expose. Run the `GRANT USAGE` and `GRANT SELECT` blocks for each schema.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `Connection refused` | Wrong host or port, or firewall blocking | Verify host/port; add Supaboard IPs to firewall allow-list |
| `password authentication failed` | Wrong username or password | Double-check credentials; confirm the user exists in this database |
| `database "X" does not exist` | Database name typo | Check database name in your RDS/Cloud SQL console |
| `SSL connection required` | Server requires SSL but toggle is off | Enable the SSL toggle |
| `FATAL: no pg_hba.conf entry` | Database server not configured to accept connections from this IP | Add Supaboard egress IPs to `pg_hba.conf` or use an SSH tunnel |
| `could not connect to server: Connection timed out` | Bastion/SSH host unreachable | Verify SSH host, port, and that Supaboard IPs are whitelisted on the bastion |
# Redshift
Source: https://docs.supaboard.ai/data-sources/connectors/redshift
Connect Supaboard to an Amazon Redshift cluster or Serverless workgroup for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → AWS Redshift** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supaboard connects from a fixed set of egress IPs. Add them to your Redshift cluster's VPC security group before attempting a connection. The current IP list is shown inside the connector form under **Whitelist IPs**.
For clusters in a private VPC, use an SSH tunnel through a bastion host in the same VPC.
**Database user permissions** — The user you provide needs at minimum:
* `CONNECT` on the database
* `USAGE` on the target schema(s)
* `SELECT` on the tables you want to expose
See [Recommended database user permissions](#recommended-database-user-permissions) for the exact SQL.
**SSL** — Redshift enforces SSL by default. Supaboard always connects with SSL to Redshift — there is no SSL toggle for this connector.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | — | Yes | Redshift cluster endpoint or Serverless endpoint (see below) |
| **Port** | `5439` | Yes | Redshift port |
| **Database** | — | Yes | Database name |
| **Schema** | `public` | No | Default schema for queries |
| **Username** | — | Yes | Database user |
| **Password** | — | Yes | Password for the database user |
> **SSL** — Supaboard always uses SSL when connecting to Redshift. No toggle is needed.
## SSH Tunnel fields
Shown when the **SSH Tunnel** toggle is enabled.
| Field | Default | Required | Description |
| - | - | - | - |
| **SSH Host** | — | Yes | Hostname or IP of the SSH bastion server |
| **SSH Port** | `22` | Yes | SSH port on the bastion server |
| **SSH Username** | — | Yes | SSH login username |
| **SSH Connection Type** | `Private Key` | Yes | `Private Key` or `Password` |
| **SSH Private Key** | — | If Private Key | PEM-encoded private key |
| **SSH Passphrase** | — | No | Passphrase for an encrypted private key |
| **SSH Password** | — | If Password | Password for SSH password authentication |
## Finding your connection details
### Amazon Redshift Console
**Provisioned cluster endpoint:**
1. Open the [Redshift Console](https://console.aws.amazon.com/redshiftv2/) and go to **Clusters**.
2. Select your cluster.
3. Under **General information**, copy the **Endpoint** — it looks like:
```text theme={null}
cluster-name.xxxxxxxxxx.region.redshift.amazonaws.com:5439/database
```
4. Use the hostname part (before the colon) as **Host**, `5439` as **Port**, and the database name (after the final `/`) as **Database**.
**Documentation:** [Connecting to a cluster](https://docs.aws.amazon.com/redshift/latest/mgmt/connecting-to-cluster.html) · [Authorizing inbound access](https://docs.aws.amazon.com/redshift/latest/mgmt/managing-clusters-vpc.html)
**Redshift Serverless endpoint:**
1. In the Redshift Console, go to **Serverless dashboard**.
2. Select your workgroup.
3. Under **Workgroup details**, copy the **Endpoint** — it looks like:
```text theme={null}
workgroup-name.accountid.region.redshift-serverless.amazonaws.com:5439/database
```
4. Parse the same way as provisioned clusters above.
**Documentation:** [Connecting to Redshift Serverless](https://docs.aws.amazon.com/redshift/latest/mgmt/serverless-connecting.html)
**VPC and security group:**
1. On the cluster or workgroup detail page, find the **VPC security group**.
2. Open the security group in the EC2 Console.
3. Add an inbound rule: **Type** = Custom TCP, **Port** = 5439, **Source** = each Supaboard egress IP (as `/32` CIDR rules).
## SSH Tunnel
Use an SSH tunnel when your Redshift cluster is in a private VPC with no public endpoint.
**When to use it:**
* Cluster is not publicly accessible (most production configurations)
* Security policy requires all external traffic to pass through a bastion host
**Checklist:**
* [ ] The bastion host is in the same VPC as the Redshift cluster
* [ ] The bastion host's security group allows SSH (port 22) from Supaboard egress IPs
* [ ] The Redshift cluster's security group allows port 5439 from the bastion host's security group
* [ ] The SSH user has permission to forward connections
* [ ] If using a private key, it is in PEM format
## IPsec Site-to-Site VPN
If your database lives on a private network, you can peer your VPN gateway with Supaboard over IKEv2 IPsec instead of exposing the database or running a bastion host. Enable the **Use IPsec site-to-site VPN** toggle in the connector form.
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway — or a per-server mapping for providers with one tunnel endpoint per peer (OCI/AWS managed VPN), e.g. `=,…` |
| **Pre-Shared Key** | Yes | The PSK configured on your gateway for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route. Defaults to your database host `/32` |
**Checklist:**
* [ ] Your VPN gateway has one IKEv2 + pre-shared-key tunnel per Supaboard IP (the IPs shown under **Whitelist IPs**)
* [ ] The gateway allows UDP 500, UDP 4500, and ESP (IP protocol 50) from those IPs
* [ ] The **Host** field is the database's **private IP address** — private DNS names don't resolve from Supaboard
* [ ] Enter the database name manually — auto-discovery doesn't run through the VPN before the connection is saved
Gateway settings, supported ciphers, a strongSwan example, and troubleshooting: [IPsec Site-to-Site VPN guide](/data-sources/security/ipsec-vpn).
## Recommended database user permissions
```sql theme={null}
-- Create a dedicated read-only user
CREATE USER supaboard WITH PASSWORD 'strong_password_here';
-- Grant connect on the database (run as superuser)
GRANT CONNECT ON DATABASE your_database TO supaboard;
-- Grant schema access
GRANT USAGE ON SCHEMA public TO supaboard;
-- Grant table access
GRANT SELECT ON ALL TABLES IN SCHEMA public TO supaboard;
-- Grant access to future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO supaboard;
```
Replace `public` with each schema you want to expose.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `Connection refused` or timeout | VPC security group blocking Supaboard IPs | Add Supaboard egress IPs to the Redshift security group |
| `FATAL: password authentication failed` | Wrong username or password | Verify credentials in the Redshift Console under **Database users** |
| `FATAL: database "X" does not exist` | Database name typo | Check database name in the Redshift Console |
| `Endpoint not publicly accessible` | Cluster has no public IP | Enable **Publicly accessible** on the cluster, or use an SSH tunnel |
| `SSL SYSCALL error` | SSL certificate issue | Redshift uses a public AWS CA certificate — no manual cert upload needed |
# Snowflake
Source: https://docs.supaboard.ai/data-sources/connectors/snowflake
Connect Supaboard to a Snowflake account for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → Snowflake** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Snowflake is cloud-hosted and generally accessible over the internet. No firewall changes are needed in most configurations. If your Snowflake account uses **network policies**, add Supaboard's egress IPs to an allowed policy. The current IP list is shown inside the connector form under **Whitelist IPs**.
**Account permissions** — The role used to connect needs at minimum:
* `USAGE` on the warehouse
* `USAGE` on the database
* `USAGE` on the target schema(s)
* `SELECT` on the tables you want to expose
See [Recommended database user permissions](#recommended-database-user-permissions) for the exact SQL.
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | — | Yes | Your Snowflake account identifier (see below) |
| **Username** | — | Yes | Snowflake user login name |
| **Password/PAT** | — | Yes | User password or personal access token |
| **Database** | — | No | Database name; Supaboard auto-discovers if left blank |
| **Schema** | — | No | Schema name; Supaboard auto-discovers if left blank |
| **Warehouse** | `COMPUTE_WH` | No | Virtual warehouse used for queries |
## Finding your connection details
### Snowflake Console
**Account identifier (Host)**
Your account identifier is in the format `orgname-accountname`. To find it:
1. Log in to [app.snowflake.com](https://app.snowflake.com).
2. Click your account name in the bottom-left corner.
3. Hover over your account to reveal the full account identifier.
4. It follows the pattern `ORGNAME-ACCOUNTNAME` (e.g. `acme-prod123`).
Use this as your **Host** value in Supaboard (without `.snowflakecomputing.com`).
**Documentation:** [Account identifiers](https://docs.snowflake.com/en/user-guide/admin-account-identifier) · [Connecting to Snowflake](https://docs.snowflake.com/en/user-guide/connecting)
**Username**
Your Snowflake login username. Administrators can find all users under **Admin → Users & Roles**.
**Warehouse**
Virtual warehouses are listed under **Admin → Warehouses**. The default is typically `COMPUTE_WH`. Supaboard uses this warehouse to execute queries — choose an appropriately sized one.
> **Warehouse cost** — Each query Supaboard runs consumes warehouse credits while the warehouse is running. Use an auto-suspend setting (e.g. 1–5 minutes) on the warehouse to minimise idle credit consumption.
**Documentation:** [Virtual warehouses overview](https://docs.snowflake.com/en/user-guide/warehouses-overview) · [Auto-suspend and auto-resume](https://docs.snowflake.com/en/user-guide/warehouses-considerations)
## Password vs Personal Access Token
Supaboard accepts either a **password** or a **personal access token (PAT)** in the **Password/PAT** field.
**Password** — the standard Snowflake user password. Simple to set up but not recommended for service accounts in security-sensitive environments.
**Personal Access Token** — a long-lived token scoped to a specific user. Preferred for service account connections.
To create a PAT:
1. In Snowflake, go to **User Menu → My Profile → Personal Access Tokens**.
2. Click **Generate Token**, set an expiry, and copy the token value.
3. Paste the token into the **Password/PAT** field in Supaboard.
**Documentation:** [Programmatic access tokens](https://docs.snowflake.com/en/user-guide/programmatic-access-tokens)
## Recommended database user permissions
```sql theme={null}
-- Create a dedicated role and user
CREATE ROLE supaboard_role;
CREATE USER supaboard_user
PASSWORD = 'strong_password_here'
DEFAULT_ROLE = supaboard_role
DEFAULT_WAREHOUSE = COMPUTE_WH;
GRANT ROLE supaboard_role TO USER supaboard_user;
-- Grant warehouse access
GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE supaboard_role;
-- Grant database and schema access
GRANT USAGE ON DATABASE your_database TO ROLE supaboard_role;
GRANT USAGE ON ALL SCHEMAS IN DATABASE your_database TO ROLE supaboard_role;
GRANT SELECT ON ALL TABLES IN DATABASE your_database TO ROLE supaboard_role;
-- Grant access to future objects automatically
GRANT USAGE ON FUTURE SCHEMAS IN DATABASE your_database TO ROLE supaboard_role;
GRANT SELECT ON FUTURE TABLES IN DATABASE your_database TO ROLE supaboard_role;
```
Replace `your_database` and `COMPUTE_WH` with your actual values.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `Incorrect username or password` | Wrong credentials | Verify username and password/PAT in Snowflake |
| `Account does not exist` | Wrong account identifier format | Use `orgname-accountname` format without `.snowflakecomputing.com` |
| `IP not allowed` | Snowflake network policy blocking Supaboard IPs | Add Supaboard egress IPs to your Snowflake network policy |
| `Warehouse suspended` | Warehouse not auto-resuming | Enable auto-resume on the warehouse under **Admin → Warehouses** |
| `Insufficient privileges` | Role lacks USAGE or SELECT grants | Run the permission SQL above for the connecting role |
| `No active warehouse selected` | Warehouse field left blank and user has no default warehouse | Enter a warehouse name in the **Warehouse** field |
# Supabase
Source: https://docs.supaboard.ai/data-sources/connectors/supabase
Connect Supaboard to a Supabase project's underlying PostgreSQL database for direct querying, schema discovery, and AI-powered analysis.
Open **Data sources → Connect Data Source → Supabase** to use this guide. For the overall workflow and the check after setup, see [Connect a data source](/data-sources/connect-a-data-source).
## Before you connect
**Network access** — Supabase projects are accessible over the internet. You do not need to whitelist Supaboard IPs for the direct connection, but if your project has **network restrictions** enabled (under Project Settings → Network), add Supaboard's egress IPs there. The current IP list is shown inside the connector form under **Whitelist IPs**.
**SSL** — Supabase requires SSL for all direct database connections. Supaboard always connects with SSL to Supabase — there is no SSL toggle for this connector.
**Database user permissions** — Use the `postgres` superuser or create a dedicated read-only user. See [Recommended database user permissions](#recommended-database-user-permissions).
## Connection fields
| Field | Default | Required | Description |
| - | - | - | - |
| **Display Name** | — | Yes | Label shown in the Supaboard UI |
| **Host** | — | Yes | Direct connection host (format: `db.xxxxx.supabase.co`) |
| **Port** | `5432` | Yes | Direct connection port |
| **Database** | `postgres` | No | Database name; auto-discovers available databases |
| **User** | `postgres` | Yes | Database user |
| **Password** | — | Yes | Password for the database user |
> **SSL** — Supaboard always uses SSL when connecting to Supabase. No toggle is needed.
## Finding your connection details
### Supabase Dashboard
1. Open [supabase.com/dashboard](https://supabase.com/dashboard) and select your project.
2. Go to **Project Settings → Database**.
3. Under **Connection info**, find the **Direct connection** section.
4. Copy:
* **Host** — format: `db.xxxxxxxxxxxxxxxxxxxx.supabase.co`
* **Port** — `5432`
* **Database name** — `postgres`
* **User** — `postgres`
5. Your database **Password** is the one you set when you created the project. If you've lost it, go to **Database → Database Settings** and reset it.
> **Use the direct connection, not the Transaction Pooler.** The Transaction Pooler (port 6543) does not support schema discovery. Always use port `5432` with the `db.xxxxx.supabase.co` host.
**Documentation:** [Connecting to your database](https://supabase.com/docs/guides/database/connecting-to-postgres) · [Connection pooling](https://supabase.com/docs/guides/database/connection-pooling)
## Recommended database user permissions
The `postgres` user has full access and works out of the box, but for security it is best practice to create a dedicated read-only user:
```sql theme={null}
-- Create a read-only user
CREATE USER supaboard WITH PASSWORD 'strong_password_here';
-- Grant connection to the database
GRANT CONNECT ON DATABASE postgres TO supaboard;
-- Grant schema access (repeat for each schema you want to expose)
GRANT USAGE ON SCHEMA public TO supaboard;
-- Grant read access to all current tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO supaboard;
-- Grant read access to future tables automatically
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO supaboard;
```
If you use multiple schemas (e.g. `auth`, `storage`), repeat the `GRANT USAGE` and `GRANT SELECT` steps for each schema you want to expose to Supaboard.
## Troubleshooting
| Error | Likely cause | Fix |
| - | - | - |
| `password authentication failed` | Wrong user or password | Verify credentials; reset the database password in **Project Settings → Database** |
| `Connection refused` on port 5432 | Using pooler host/port instead of direct | Use host format `db.xxxxx.supabase.co` and port `5432`, not the pooler |
| `SSL required` | Supabase requires SSL | Supaboard uses SSL automatically — this error should not occur; contact support if it does |
| Schema discovery returns no tables | User lacks schema USAGE or SELECT grants | Run the grant SQL above for the `supaboard` user |
| `project is paused` | Free-tier Supabase project is paused | Visit the Supabase Dashboard and resume the project |
# Data Sources
Source: https://docs.supaboard.ai/data-sources/overview
Connect your databases and apps, then explore the data available to your team.
A **Data Source** is a live connection from Supaboard to an external database or service. Every widget on a dashboard, every query run through [the Analyst](/analyst/overview), and every Agent's knowledge of your data flows through these connections.
## Before you begin
Choose the database or app you want to analyze. To add a connection, you need permission to manage data sources and the connection details from its administrator.
Follow [Connect a data source](/data-sources/connect-a-data-source) for the complete setup workflow.
## The Connections Page
**Settings → Connections** is the administrative home for all your data source connections.
Each connection is shown as a card displaying:
* The connector logo
* The connection name
* Table count (e.g. *5 tables*)
Click any card to open its **Connection Detail** panel, which has five tabs: **Info**, **Tables**, **Summary**, **Standard Instructions**, and **Advanced**.
**Search** connections by name or connector type using the search bar.
## The Data Source Browser
The **Data sources** section in the main sidebar is the analyst-facing view — browse, explore, and interact with your connected data.
### Filtering connections
Use the search field to find a connection, the sort menu to change ordering, or **Favourites** to return to sources you have marked. Each source shows its status and table count.
### View modes
Toggle between **Grid** and **Table** views.
### Item actions
From any data source item's action menu:
| Action | Description |
| - | - |
| **Open** | Navigate to the table browser for this connection |
| **Rename** | Change the display name |
| **Favourite** | Pin to the top of the list |
| **Ask Stella** | Start an AI conversation scoped to this connection |
| **Delete** | Remove the connection |
> The **Ask Stella** action opens [the Analyst](/analyst/overview) — the button label hasn't caught up with the feature's name yet.
## Adding a Connection
Open **Data sources → Connect Data Source**, or use the connection action in **Settings → Connections**. A searchable grid shows all available connectors — select one to open its configuration form.
### Direct database connections
For databases (PostgreSQL, Snowflake, etc.), you connect directly by entering your host, credentials, and any advanced options (SSL, SSH tunnel). See [Available Connectors](#available-connectors) for the full list.
### Non-database connections via Fivetran
For SaaS tools and third-party services (Shopify, Google Analytics, Airtable, Stripe, and others), Supaboard uses **Fivetran** as the ETL layer. Fivetran handles syncing data from those services into a queryable form — you authenticate with the source service through Fivetran's OAuth flow, and Supaboard takes care of the rest.
The two kinds behave differently at question time. A direct database is queried live, so an answer reflects the data as it is now. A Fivetran source is batch: the first sync lands within a day, and after that it refreshes on a schedule, hourly to daily, so an answer reflects the last completed sync. See [Fivetran](/data-sources/connectors/fivetran).
Some of these connectors are included on every plan and some require an upgrade — see [Available Connectors](#available-connectors). The connector grid tells you which is which before you commit: a connector that needs an upgrade shows an upgrade prompt when you select it.
### What happens after you save
1. Supaboard creates the connection record.
2. Tables in the database are discovered automatically.
3. Schema generation runs in the background — column metadata is extracted for each table.
4. The connection becomes queryable by agents and widgets once schema generation completes.
> For large databases with many tables, schema generation runs as a background job. You can navigate away and return — the connection will show a *Pending* status until it's ready.
## Available Connectors
The connector grid holds well over a hundred integrations and is searchable by name. The tables below cover the ones with a dedicated setup guide, plus which are included on every plan.
### Direct database connectors — all plans
| Connector | Notes |
| - | - |
| [**PostgreSQL**](/data-sources/connectors/postgres) | Supports SSH tunnel, SSL, JDBC params |
| [**MySQL**](/data-sources/connectors/mysql) | Supports SSH tunnel, SSL, JDBC params |
| [**Snowflake**](/data-sources/connectors/snowflake) | Includes warehouse and schema auto-discovery |
| [**BigQuery**](/data-sources/connectors/big-query) | OAuth or Service Account JSON authentication |
| [**Amazon Redshift**](/data-sources/connectors/redshift) | Supports SSH tunnel, SSL |
| [**MongoDB**](/data-sources/connectors/mongodb) | Connection string (URI) based |
| [**ClickHouse**](/data-sources/connectors/clickhouse) | — |
| [**Databricks**](/data-sources/connectors/databricks) | Personal access token + HTTP path |
| [**Supabase**](/data-sources/connectors/supabase) | SSL on by default |
| [**Microsoft SQL Server**](/data-sources/connectors/microsoft-sql-server) | Supports SSH tunnel, SSL, JDBC params |
| **MotherDuck / DuckDB** | Database, schema, and MotherDuck token |
| [**Oracle**](/data-sources/connectors/oracle) | Supports IPsec VPN |
### Fivetran-powered connectors
These sync through Fivetran. The following are **included on every plan**, alongside the direct databases above:
Google Analytics · Typeform · Calendly · Stripe · Linear · Asana · YouTube Analytics · Notion · Auth0 · Airtable
Everything else in the catalogue — including Shopify — requires an upgrade. Selecting one shows the upgrade prompt with the plan you'd need; see [pricing](https://supaboard.ai/pricing).
[Learn more about Fivetran-powered connectors here.](/data-sources/connectors/fivetran)
### Fivetran sources keep their own identity
A source synced through Fivetran lands in a shared warehouse, but Supaboard attributes it to the **original service**. A Stripe-via-Fivetran connection shows Stripe's name and logo everywhere — the data source card, the connection header, the resource picker — rather than the warehouse it physically lives in.
## Testing a Connection
Every connector form has a **Test Connection** button in the footer. Click it before saving to verify that Supaboard can reach the database with the credentials provided.
* On success: a toast confirms the database is responding.
* On failure: the error message from the database is shown, helping you diagnose the issue (wrong password, firewall block, incorrect port, etc.).
> Always test before saving. A saved connection that can't connect will show as *Failed* and block agents and widgets that depend on it.
## Connection Detail — Info Tab
Open any connection card to see the **Info** tab, which shows live diagnostics.
### Performance metrics
* Response time
* Query latency
* Connection pool stats
### Security status
* SSL/TLS enabled state
* Encryption status
* Authentication type
### Connection pool *(direct database connectors only)*
* Active connections
* Idle connections
* Maximum pool size
> Pool status is not shown for Fivetran-backed sources. Supaboard does not hold a direct connection to them, so there is no pool to report.
### Tunnel status *(SSH tunnel connections)*
* Tunnel connection state
* Tunnel latency
### Database info
* Database version, charset, and collation
* Visible only to users with resource access
### Warnings & errors
Any active warnings are listed. Errors are shown in a red alert box with details.
## Connection Detail — Tables Tab
The **Tables** tab is where you manage which tables Supaboard knows about and what it knows about them.
### Table list
Every table discovered in the database is listed here. For each table:
| Action | What it does |
| - | - |
| **Hide / Show** | Toggle whether this table appears in the data browser and is accessible to agents and widgets |
| **Regenerate Schema** | Re-scans this table's columns and rebuilds its metadata |
The footer shows a count of how many tables are currently hidden.
### Missing schema banner
If any tables are missing schema (column metadata hasn't been generated), a yellow banner appears at the top. Click **Regenerate Schemas** to rebuild all of them at once.
> Schema generation polls in the background every 20 seconds. A loading overlay appears during bulk regeneration.
### Remapping tables
Click **Remap Tables** to re-scan the database and discover any tables that have been added or removed since the connection was created. New tables are added to the list; removed tables are flagged.
## Connection Detail — Summary Tab
The **Summary** tab holds a free-text description of the entire database — its purpose, what systems write to it, and any high-level context that helps your agents and your team understand what it contains.
Write in plain language. This summary is read by agents when deciding whether and how to query this data source.
## Connection Detail — Standard Instructions Tab
**Standard Instructions** are rules that apply to every query written against this connection, regardless of which agent asks. Use them for conventions that belong to the *database* rather than to any one agent:
* Which column to join on when several look plausible
* A soft-delete convention every query must respect — *"always exclude rows where `is_archived = true`"*
* A canonical date column when a table has several
* Dialect quirks specific to this deployment
> The difference between this and an agent's [Rules](/agents/overview#rules): standard instructions travel with the *connection*, so every agent that includes this source inherits them. Agent rules travel with the *agent*, across whatever sources it can see.
## Connection Detail — Advanced Tab
The **Advanced** tab contains operations that modify or remove the connection.
### Rename connection
Update the display name that appears across the UI, in agents, and in widget configurations.
### Regenerate schema
Rebuilds schema metadata for all tables in this connection. Use this if your database structure has changed significantly (new columns added, types changed, tables restructured).
> This operation is permanent and cannot be undone — existing schema metadata is replaced.
### Delete connection
Permanently removes the connection from Supaboard. A confirmation is required.
**What happens on deletion:**
* The connection and its table metadata are removed.
* Dashboards with widgets that queried this connection will show errors on those widgets.
* Agents that included this connection in their scope lose access to it.
> Deletion cannot be undone.
## Team Access Control
By default, all workspace members can query any data source. You can restrict a specific connection to only certain team members.
Open the connection's **Advanced** tab and scroll to **Team Access**. Every non-deleted workspace member is listed with a toggle:
| State | Meaning |
| - | - |
| **Toggle on** | This member can query this connection |
| **Toggle off** | This member cannot query this connection |
| **Owner** (locked on) | Owners always have access and cannot be removed |
Changes are saved automatically with a short debounce.
### How this interacts with agent access
If a team member is blocked from a data source but an agent they use includes that source in its scope, the member will not be able to retrieve data from that source through the agent. The restriction is enforced at the connection level, regardless of agent configuration.
## Schema Generation
Schema generation is the process of scanning a table's columns, types, and relationships to build the metadata that agents use to write accurate queries.
### When it runs
* **Automatically** when a new connection is created.
* **On demand** via the Regenerate button (per-table or bulk).
* **After remapping** when new tables are discovered.
### Incremental context refresh
Alongside raw schema, Supaboard writes an AI-generated description of each table. Refreshing that context is **incremental**: only tables that don't already have a description are processed.
Adding one table to a 400-table database no longer means regenerating descriptions for the other 399 — which is the difference between a refresh that takes seconds and one that takes many minutes.
### What gets discovered
Beyond ordinary tables, Supaboard discovers **views, materialized views, foreign tables, and partitioned tables**. Materialized views in particular are introspected properly, so a dashboard built on a pre-aggregated matview reports real columns rather than an empty schema.
For PostgreSQL, the default `postgres` database is included in discovery alongside your own databases.
### Schema statuses
| Status | Meaning |
| - | - |
| **Generated** | Column metadata is complete and up to date |
| **Missing** | Schema hasn't been generated yet — agent queries against this table may be inaccurate |
| **Generating** | Schema is currently being built (polls every 20 seconds) |
> Tables with missing schema are flagged in the Tables tab and in any Agent that includes them. Always resolve missing schemas before using a connection in production.
## Database Context (Documentation)
Beyond raw schema, Supaboard lets you document what your data *means* at two levels: the whole database and individual tables. This context is read by agents to improve answer accuracy.
### Database-level summary
Written in the **Summary** tab. A high-level overview of the database's purpose and contents.
### Table-level context
For each table in the **Tables** tab, you can fill in four fields:
| Field | What to write |
| - | - |
| **Summary** | What this table stores and how it relates to other tables |
| **Granularity** | What each row represents — e.g. *"One row per invoice line item"* |
| **Gotchas** | Data quality issues, quirks, or caveats agents should be aware of |
| **Usecases** | Common questions this table is useful for answering |
> Context written here is shared with any Agent that includes this data source. You do not need to re-enter it per agent — it flows through automatically.
## Column Values and Filter Suggestions
When someone builds a dashboard filter or picks a value in the query builder, Supaboard fetches the distinct values for that column.
| Behaviour | Detail |
| - | - |
| **Search and paging** | Values are searched and paged at the database level for connectors that support it, so a column with a million distinct values is still usable |
| **True totals** | You see the real count of matching distinct values, not just how many are on the current page |
| **Cascading filters** | Suggestions narrow to rows matching the dashboard filters already applied — pick a country and the city list shrinks to that country |
> Cascading suggestions only apply to filters whose table matches the column being searched, and only on connectors that push the search down into SQL. On connectors that fall back to in-memory filtering, cascading filters are **ignored** and you see the unfiltered value list. If a suggestion list looks wider than expected, that fallback is the likely reason.
### Promoting a column to an enum
A low-cardinality column can be marked as an enum so filters render as a picker rather than a free-text box. Supaboard checks the column first and refuses if it has **more than 1000 distinct values** — an enum with more than that is not a usable picker.
## Connection Statuses
| Status | Indicator | Meaning |
| - | - | - |
| **Active** | Green dot | Connected and ready to query |
| **Pending** | Spinner | Connected; schema generation or sync in progress |
| **Failed** | Red indicator | Was connected but is now unreachable — check credentials or network |
| **Disconnected** | Grey | No tables have been loaded yet |
A failed connection will cause widgets and agents that depend on it to return errors. Open the connection's Info tab to see the specific error message.
> **PostgreSQL read replicas.** Reads against a hot standby are occasionally cancelled by the replica applying WAL — a transient conflict, not a real failure. Supaboard detects these and retries automatically, so replica-backed dashboards don't fail spuriously under replication load. Genuine errors are not retried.
## Egress IP Whitelist
If your database is behind a firewall, you need to allow inbound connections from Supaboard's egress IPs.
Navigate to any connector form and look for the **Whitelist IPs** section. The current list of Supaboard egress IP addresses is shown there — copy them and add them to your database's firewall allow-list before attempting a connection.
## Permissions
| Action | Minimum role |
| - | - |
| View available data sources | Viewer (with resource access) |
| Browse tables | Viewer (with resource access) |
| Create a connection | Admin |
| Edit connection name | Admin |
| Delete a connection | Admin · Owner |
| Manage team access | Owner |
| Regenerate schema | Admin |
| Remap tables | Admin |
| Edit database / table context | Admin |
| Test a connection | Admin |
Resource-level restrictions (set per connection) are enforced on top of workspace roles — a Viewer who has been blocked from a specific data source cannot query it regardless of other permissions.
# IPsec Site-to-Site VPN
Source: https://docs.supaboard.ai/data-sources/security/ipsec-vpn
Connect Supaboard to databases that live entirely inside a private network by peering your VPN gateway with Supaboard over an IKEv2 IPsec site-to-site tunnel. Once the tunnel is up, Supaboard reaches your database at its private IP address — nothing in your network needs a public IP, a bastion host, or an inbound firewall exception for database traffic.
**Available on:** Postgres, MySQL, Microsoft SQL Server, Oracle, MongoDB, ClickHouse, and Redshift.
**Not available on** Snowflake, BigQuery, and Databricks — those are vendor-hosted services with public endpoints, so there is no private network to tunnel into.
## How it works
* Supaboard runs its own IPsec gateways and **initiates the tunnel on demand** — the first time a connection is tested or used, and automatically after any outage. Your gateway can be configured as a responder only.
* Supaboard connects from multiple regions, so your gateway needs **one tunnel definition per Supaboard IP** (the same public IPs shown under **Whitelist IPs** in the connector form).
* Traffic selectors are narrow by default: `/32 ↔ /32`. Only database traffic crosses the tunnel.
* Dead-peer detection runs every 30 seconds and the tunnel re-establishes itself after network interruptions or rekeys.
* Multiple Supaboard connections that share the same gateway and pre-shared key share a single tunnel.
## Requirements
* [ ] A VPN gateway that supports **IKEv2 with pre-shared key** authentication (strongSwan, pfSense/OPNsense, FortiGate, Cisco ASA/FTD, Palo Alto, AWS Virtual Private Gateway, Azure VPN Gateway, and similar all qualify)
* [ ] Your database reachable from the gateway at a **private IP address** — enter that IP as the **Host** in the connector form. Private DNS names do not resolve from Supaboard.
* [ ] Gateway firewall allows **UDP 500 (IKE)** and **UDP 4500** from each Supaboard IP — Supaboard always UDP-encapsulates ESP (NAT-T framing), so raw ESP/protocol 50 is never required
## Configure your gateway
Create one IKEv2 tunnel per Supaboard IP with these settings:
| Setting | Value |
| - | - |
| IKE version | IKEv2 |
| Authentication | Pre-shared key |
| Peer / remote address | The Supaboard public IP (one tunnel per IP) |
| Local ID | Your gateway's public IP (or a custom ID — see below) |
| IKE proposal | AES-256 / SHA-256 / DH group 14 (modp2048), AES-128-GCM / SHA-256 PRF / DH group 19 (ecp256), **or** AES-256 / SHA-384 / DH group 20 (ecp384) |
| ESP proposal | AES-256 / SHA-256, AES-128-GCM, **or** AES-256 / HMAC-SHA1 (PFS group 20) |
| Local traffic selector | Your database host `/32` (or the subnet you enter as **Remote Subnet**) |
| Remote traffic selector | The Supaboard IP `/32` |
| NAT traversal | Must be enabled — Supaboard always uses UDP 4500 encapsulation |
| Start behavior | Responder / on-demand is fine — Supaboard initiates |
If your gateway uses a custom IKE identity instead of its public IP, contact support to have it set on the Supaboard side — by default Supaboard expects the peer ID to equal the gateway address.
### OCI managed Site-to-Site VPN
Oracle Cloud's managed VPN terminates a **separate connection per peer**: you create one CPE (customer-premises equipment) object per Supaboard IP, and each resulting IPSec connection gets its **own Oracle tunnel-endpoint IP**. Supaboard supports this natively — the **VPN Gateway Address** field accepts a per-server mapping:
1. In OCI, create one **CPE** per Supaboard IP (CPE IP = the Supaboard IP).
2. Create one **IPSec connection** per CPE on your DRG, static routing, with the route to the on-premises network set to that Supaboard IP `/32`.
3. On each connection, open **Tunnel 1**, set a **shared secret** — use the **same PSK on every tunnel** — and note the **Oracle VPN IP address** (the tunnel endpoint). OCI's usual custom-algorithm choices (phase 1 AES-256-CBC / SHA2\_384 / GROUP20, phase 2 AES-256-CBC / HMAC\_SHA1\_128 / GROUP20) are supported as-is — no need to change them.
4. In the Supaboard connector's **VPN Gateway Address** field, enter one `supaboard-ip=oracle-tunnel-ip` pair per connection, comma-separated:
```text theme={null}
139.59.30.169=129.146.1.10,157.230.31.178=129.146.2.20,64.23.156.30=129.146.3.30
```
Each Supaboard server automatically uses its own entry. The same mapping form works for any provider that assigns a distinct tunnel endpoint per peer (AWS Site-to-Site VPN behaves the same way). Tunnel 2 of each OCI connection is unused — Supaboard initiates a single tunnel per server.
### Example: strongSwan responder
```text theme={null}
connections {
supaboard {
version = 2
proposals = aes256-sha256-modp2048,aes128gcm16-prfsha256-ecp256
local {
auth = psk
id =
}
remote {
auth = psk
}
children {
supaboard {
local_ts = # e.g. 10.20.0.0/24 or 10.20.0.5/32
remote_ts = 0.0.0.0/0 # narrowed per-tunnel by Supaboard
esp_proposals = aes256-sha256,aes128gcm16
}
}
}
}
secrets {
ike-supaboard {
secret = ""
}
}
```
## Configure the Supaboard connector
In the connector form, enable **Use IPsec site-to-site VPN**:
| Field | Required | Description |
| - | - | - |
| **VPN Gateway Address** | Yes | Public IP of your VPN gateway |
| **Pre-Shared Key** | Yes | The PSK you configured for the Supaboard tunnels |
| **Remote Subnet** | No | CIDR the tunnel should route (e.g. `10.20.0.0/24`). Defaults to your database host `/32` |
Then set the **Host** field to the database's **private IP address** and use **Test Connection**. Tunnel negotiation happens during the test — an IKE failure (wrong PSK, blocked port) is reported directly in the test result.
Database auto-discovery (the database/schema dropdowns) does not run through the VPN before the connection is saved — enter the database name manually and rely on **Test Connection**.
Once connected, the connection details page shows the live tunnel state (`tunnel_type: ipsec`, `tunnel_active`), which distinguishes a tunnel outage from a database outage.
## IPsec or SSH tunnel?
| | SSH Tunnel | IPsec Site-to-Site VPN |
| - | - | - |
| You need | A bastion host with SSH reachable from Supaboard | An IKEv2-capable VPN gateway |
| Exposure | Bastion's SSH port open to Supaboard IPs | Only IKE/ESP open to Supaboard IPs |
| Reach | One forwarded host/port per connection | Any database the tunnel's subnet covers |
| Best for | One-off databases, existing jump-host setups | Network-level policies, multiple databases, no-SSH environments |
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| Test fails with `ipsec: initiate: AUTHENTICATION_FAILED` | PSK or IKE identity mismatch | Verify the PSK and that your gateway's local ID is its public IP (or ask support to register a custom ID) |
| Test fails with `ipsec: initiate` timeout | IKE traffic not reaching your gateway | Allow UDP 500/4500 and ESP from every Supaboard IP; confirm the gateway address |
| `database host … must be an IP address when using IPsec` | Host field contains a DNS name | Use the database's private IP, or set **Remote Subnet** explicitly |
| Test succeeds intermittently across retries | Only some Supaboard IPs have tunnels configured | Create one tunnel per listed Supaboard IP — connections may originate from any of them |
| Connection info shows `tunnel_active: false` and queries fail | Tunnel down (gateway reboot, rekey failure) | Check gateway logs; Supaboard re-initiates automatically on the next use |
| Connection works but a second Supaboard connection to the same private IP errors with "already routed to a different VPN gateway" | The same private address is claimed via two different gateways | Use the same gateway for both, or distinct addresses/subnets — contact support if you believe this is in error |
| No traffic flows although the tunnel is up | Traffic selector mismatch | Ensure your local selector covers the database host and the remote selector covers the Supaboard IP (`/32`) |
# Create a Datasite
Source: https://docs.supaboard.ai/datasites/create-a-datasite
Build an interactive site from a clear brief and your connected data.
Use a Datasite when your audience needs a designed page with its own layout, navigation, and interactive data. Start with a brief, review the preview, and publish after it is ready.
## Before you begin
You need permission to create Datasites and an agent with access to the data you want to use. Prepare the audience, key metrics, date range, and any reference files or brand assets.
## 1. Start from Artifacts
Open **Artifacts → New Artifact → Datasite**. Enter a **Name** and **Description**, and select a project if this work belongs with an existing initiative. Choose **Build with AI** to continue with your brief.
You can also use the **Datasite** shortcut on Home or ask for a Datasite in an existing analysis chat. Choose **Build Manually** in the artifact form when you want to start in the editor yourself.
## 2. Describe the page
Give the Analyst a specific outcome:
> Create a revenue review site for our operations team. Show completed orders and net revenue for the last 12 months, followed by a category breakdown. Add a date filter and a table of the largest refunds. Use a simple light layout and explain each metric.
Name the source or agent when several could fit. Attach a logo or reference document if it should shape the result. Check any questions about metric definitions before continuing.
## 3. Build and review
In the Datasite editor, **Build** creates or changes the site. Use **Plan** for a proposed approach first, or **Ask** to ask questions without changing the site.
After a successful build, use **Preview** to inspect the page. Check the **Desktop**, **Tablet**, and **Phone** sizes, try its filters, and compare key numbers with a known result. The **Glossary** tab holds the saved queries behind the page.
## 4. Refine a specific part
Use **Select & edit elements** in the preview toolbar, then click the part you want to change. Describe the change in the chat, such as “Show the comparison period under this KPI.” Review the new preview before continuing.
If a build fails, read the error and use **Fix with AI**. A failed preview does not replace the published version.
## Expected result
You have a working preview with the intended layout, data, and filters. It is ready for review by your team; publishing is a separate step.
## Next steps
[Publish and share the site](/datasites/publish-and-share), add [custom domains](/datasites/datasite-custom-domains), or manage [variables](/datasites/datasite-variables).
# Datasite Custom Domains
Source: https://docs.supaboard.ai/datasites/datasite-custom-domains
Every published datasite gets its own URL. Here's how that works, and how to serve one from a domain you own.
A published [datasite](/datasites/overview) is served from its own hostname. You get one automatically, and you can add your own on top — `dashboards.yourcompany.com` pointing at your datasite, with the TLS certificate handled for you.
Everything here lives in the **Publish** popover in the datasite editor.
## Before you begin
Publish a successful Datasite build first. You need permission to manage its publishing settings and access to your domain provider's DNS settings. Use [Publish and share](/datasites/publish-and-share) to review the site's audience before distributing a custom link.
## The address you get for free
The first time you publish a datasite, it's assigned a subdomain built from its name plus a short random tail:
```
revenue-overview-x7k2p.supaboard.live
```
A few things worth knowing about this address:
* It's minted **once**, at first publish. Renaming the datasite later doesn't change it — links people already have keep working.
* Embed links use it too. The URL you copy from the publish popover is this origin.
* In the domain list it appears with an **Auto** tag and no remove button. It's the datasite's canonical home and can't be deleted.
Datasites published before this feature existed pick up their address the next time you hit **Publish** or **Update**.
## Adding your own domain
In the publish popover, under **Add a custom domain to this datasite**:
1. Type the hostname you want — the placeholder suggests the shape: `dashboards.yourcompany.com` — and press Enter or click **Add**.
2. The popover shows the DNS record to create, with **Type / Name / Value / Proxy** columns. Every cell is click-to-copy, since each one gets retyped into a DNS provider.
3. Create that record with your DNS provider.
4. Watch the status dot next to the hostname. When it turns green, the domain is live.
The record is a single CNAME pointing at the datasite's assigned subdomain:
| | Subdomain (`dashboards.acme.com`) | Root domain (`acme.com`) |
| - | - | - |
| Type | `CNAME` | `ALIAS` / `ANAME` |
| Name | `dashboards` | `@` |
| Value | the datasite's assigned subdomain | the datasite's assigned subdomain |
| Proxy | Off | Off |
A root domain can't hold a CNAME — that's a DNS rule, not ours — so at the apex you need a provider that supports ALIAS or ANAME records (or use a subdomain, which is simpler).
There's no TXT record and no ownership-verification step. Registering a hostname is inert until the domain's real owner points DNS at it, so there's nothing to verify.
### The certificate
You don't request one. When the first HTTPS request arrives on your hostname, a certificate is issued for it automatically. That usually happens within a minute of DNS propagating.
> **The record must be plain DNS — no CDN proxying.** If your provider has an orange-cloud/proxy toggle (Cloudflare and friends), turn it **off** for this record. A proxied record terminates TLS at the CDN, which means the certificate for your hostname can never be issued, and the domain will sit at "TLS handshake failed" forever.
## Reading the status dot
Each custom domain row has a status dot, checked every time you open the popover:
| Dot | Meaning |
| - | - |
| Grey, pulsing | Checking the hostname now |
| **Green** | Live — the domain is serving this datasite. On a private datasite the message notes that visitors get the sign-in page first, which is correct behavior. |
| **Red** | Something's wrong, and the message under the row says what |
| Flat grey | The check itself couldn't run — try again |
The red states are specific, and each one tells you the fix:
* *"That hostname does not resolve yet"* — the DNS record is missing or still propagating. Give it a few minutes.
* *"The TLS handshake failed, so no certificate has been issued"* — almost always a proxied record. Switch it to DNS-only.
* *"Something answered on that hostname but it is not this datasite"* — the record points somewhere else, or a CDN is answering in front of us.
* *"That hostname is serving a different datasite"* — the record's Value is another datasite's subdomain.
* *"No answer within 12s"* — DNS may still be propagating, or the certificate is mid-issuance. Try again shortly.
## Rules and limits
* Hostnames must be fully qualified (`dashboards.acme.com`, not `dashboards`) and can't be IP addresses.
* A hostname can be registered to **one** datasite across the whole platform. Registering it elsewhere returns "already registered".
* Hostnames under the platform's own datasite domain are assigned automatically and can't be registered by hand.
* Adding and removing domains requires editing access to the Datasite and an editing role in the workspace.
* Removing a domain takes effect immediately: the hostname stops serving the datasite and its certificate stops renewing. The DNS record at your provider is yours to clean up.
### Private datasites on custom domains
Visibility follows the datasite, not the domain. A private datasite stays private on your custom domain — visitors get the sign-in page, including the **Continue with Google** and **Continue with Microsoft** options, and only signed-in people with access to the Datasite get in. See [Publishing and Embedding](/datasites/overview#publishing-and-embedding).
## Expected result
The domain status is green and the hostname opens the intended published site. Its visibility and access remain those of the Datasite.
# Datasite Variables
Source: https://docs.supaboard.ai/datasites/datasite-variables
Give a datasite API keys and configuration values — public ones ship with the page, secret ones never leave the server.
A [datasite](/datasites/overview) sometimes needs a credential: an analytics key, a maps token, an API key for a service you want the page to call. Variables are where those live. You store the value once; the page references it by name; the AI can write code against it without ever seeing it.
Open the manager from the **key icon** in the datasite editor toolbar, next to the environment picker. The modal is titled **Datasite Variables**.
## Before you begin
You need editing access to the Datasite and the configuration value supplied by the service you want to use. Decide whether the value is safe for visitors to see. Keep confidential credentials in **Secret** variables.
## Public or secret
Every variable is one or the other, and the choice is about where the value is allowed to go:
| | Public | Secret |
| - | - | - |
| Where the value lives | Injected into the served page | On the server, sealed |
| Who can see it | Anyone who opens the site | Not returned to the browser or variable editor |
| How code uses it | `sbEnv("NAME")` | `sbApi("NAME", ...)` |
| Good for | Publishable keys — analytics, maps, publishable payment keys | Anything that must stay confidential |
The form's own description of Secret is the whole model in one line: *"Never sent to the browser. The backend makes the call for you, and only to the host below."*
The manager's footer states the one caveat that matters, and it's worth quoting because people miss it:
> The AI sees these names, never the values. Public values ship inside the page; secret values stay on the server, but anyone who can open this site can trigger calls that use them.
That last clause is the real security boundary. A visitor to your datasite can never *read* a secret, but they can cause the page to make the API calls the page is built to make. If those calls cost money or mutate data, publishing the datasite publicly means anyone with the link can trigger them.
## Creating a variable
**New Variable** in the manager opens the form:
* **Name** — `UPPER_SNAKE_CASE`, up to 64 characters. The field uppercases as you type. Names are how code refers to the variable, so they're **immutable after creation** — to rename one, delete it and recreate it.
* **Type** — Secret (the default) or Public.
* **API host** — secret variables only, required. A bare hostname like `api.example.com`: no `https://`, no path. This is the *only* place the value will ever be sent, and it's pinned at save time — the page can't redirect a secret elsewhere later. Private and loopback addresses are rejected.
* **Value** — pasted once. See below.
* **Description** — optional, but useful: it's shown to the AI so it knows what the key is for. The value never is.
### Values are write-only
This is the property everything else hangs off:
* Once saved, a value is never returned again — not to the edit form, not to the AI, not to any API. The list shows a last-4 preview (`••••a1b2`) so you can tell keys apart.
* Editing a variable and leaving the value blank keeps the stored value. Pasting a new one replaces it.
* Switching a variable between public and secret requires re-entering the value, because the two are stored differently.
* Deleting is immediate and confirmed with: *"Delete this variable? Any code that uses it will stop working."* It means it.
Public values take effect on the **next page load** — rotate an analytics key and every visitor picks it up without a republish.
## Using variables in the page
The datasite SDK exposes both kinds. You usually won't write this yourself — you ask the analyst and it does — but this is what the code looks like.
**Public** values are synchronous reads:
```js theme={null}
const key = sbEnv("ANALYTICS_KEY"); // "" if unset
```
**Secret** values never appear in code at all. Instead the code makes a proxied request and writes `{{NAME}}` where the credential belongs:
```js theme={null}
const res = await sbApi("WEATHER_KEY", {
path: "/v1/forecast?city=Berlin&apikey={{WEATHER_KEY}}",
method: "GET",
});
const data = await res.json();
```
The server substitutes the real value into the placeholder and sends the request to the variable's pinned host. The browser sees the response, never the key.
A few properties of the proxy worth knowing:
* The destination is always the pinned host. The page can choose the path, method, headers, and body, but not where the request goes.
* Placeholders are substituted in headers and the query string only, and only for that variable.
* Redirects are not followed, request bodies are capped at 1 MB, and requests time out at 120 seconds. Streaming responses (SSE) pass through, so AI-provider endpoints work.
* If the upstream call fails, the page gets a generic `the upstream request to failed` — the real error is withheld because it could echo the substituted URL back.
## The AI and your variables
The analyst is told each variable's **name, type, host, and description** on every build turn. Values are excluded outright — there is no way to phrase a prompt that makes it print one, because it was never given one.
It works the other direction too: if you ask for something that needs a credential ("add a live weather panel"), the analyst can **request a variable**. That creates an empty, named slot — no value, and the build doesn't pause for it. You'll see:
* a **Needs a value** pill on the variable in the manager, and
* the toolbar key icon turns purple with a dot badge, tooltip *"N variables need a value"*.
The generated code is written against the empty slot, so it starts working the moment you paste the value in. Until then, calls through it fail with *"variable X has no value yet"*.
## Signed-in viewers
Related, and often used together with variables: on a **private** datasite the SDK exposes who's viewing.
```js theme={null}
const { first_name, email, role } = sbUser() ?? {};
```
`sbUser()` (and the `useSbUser()` hook) returns the signed-in viewer's name, email, and role on private datasites — and `null` on public datasites and embed links, so anything personalised needs a fallback. Ask the analyst for *"greet the viewer by name"* and it handles both cases.
## Expected result
The variable appears by name in the manager. Ask the Analyst to use that name, then review the preview before [publishing changes](/datasites/publish-and-share).
# Datasites
Source: https://docs.supaboard.ai/datasites/overview
Build an interactive analytics site, then publish and share it.
A **Datasite** is a custom analytics site that an AI analyst builds for you out of your own data. You describe what you want in plain English; it writes the queries, designs the pages, and gives you a live site you can publish and embed.
Where a [dashboard](/dashboards/overview) is a grid of widgets you arrange yourself, a datasite is a designed page — your layout, your sections, your narrative — that you shape by talking to it. Under the hood it's a real React application with real saved queries, and you can read and edit both.
Use **Artifacts → Datasites** to find your sites. Availability depends on your workspace plan and access.
## Before you begin
You need Datasites feature access and permission to create a site. Choose an agent with access to its data, and prepare the audience, metrics, and any reference files.
Turn a brief into an interactive preview.
Review the audience and put the site live.
## Creating a Datasite
Open **Artifacts → Datasites** to find existing sites. Choose **New Artifact → Datasite**, enter a name and description, optionally select a project, and choose **Build with AI** to open the Analyst with your brief.
Use **Build Manually** if you want to start with an empty site in the editor. You need permission to create a Datasite and access to the data it will use.
### Other ways in
* Use the **Datasite** shortcut on Home, complete the prompt, and send it.
* Ask for a Datasite in an existing Analyst chat.
* Use **Create as Datasite** from a dashboard's options menu to start from its charts, tables, and KPIs. Use **Add Page** in that dialog to include more than one dashboard page.
In the Datasite editor, you can attach supporting files, select an agent, and use the **Build**, **Plan**, or **Ask** modes described below.
### Writing a good first prompt
The analyst works from what you tell it, and the first message sets the shape of everything that follows. Worth including:
* **Who it's for** — *"for our customer success team"*, *"a public status page for our customers"*
* **What sections you want** — *"a KPI row, then revenue over time, then a filterable table of accounts"*
* **How it should be filterable** — *"filter by region and plan tier"*
* **Anything visual** — *"use our brand blue, dark background"* (attach a logo and it'll use it)
You can course-correct at any point, so don't over-plan. But a prompt with structure in it saves a round trip.
## The Library
Open **Artifacts → Datasites** to browse your sites. Search by name, filter by project or ownership, and switch between **Grid** and **Table** views. Status distinguishes draft and published sites.
Open a site to work on it. Its **⋯** menu contains the editing, sharing, and management actions available to you. See [Artifacts](/artifacts/overview) for the shared library controls.
## The Editor
The editor is split: the analyst chat on the left, your datasite on the right. Drag the divider to resize.
A brand-new datasite starts with the chat filling the window and reveals the right-hand pane once the first version has built.
**Toolbar:**
| Control | What it does |
| - | - |
| Back arrow | Returns to the library |
| Title + pencil | Rename the datasite in place |
| **Share** | Invite workspace members |
| **Publish** | Make the current build live (see [Publishing](#publishing-and-embedding)) |
**Above the right-hand pane:**
| Control | What it does |
| - | - |
| **Preview / Code / Glossary** | Switch between the running site, its source, and its queries |
| **Desktop / Tablet / Phone** | Preview at different widths, with a device frame |
| Environment picker | Run the preview against a different data source mapping |
| **Variables** (key icon) | API keys and config values for the site — see [Datasite Variables](/datasites/datasite-variables) |
| **Select & edit elements** | Click something in the preview to talk about it |
| **Refresh this view** | Rebuild the preview, or refetch files and queries |
| Panel toggle | Hide or show the chat |
## Working With the Analyst
### Conversations
The analyst keeps separate conversation threads. The panel header shows the current thread's name — click it to switch threads or start a **New conversation**, and use the pencil and trash icons to rename or delete one.
* A new thread isn't created until you actually send something, and it names itself from your first message
* Opening the editor lands you in your most recently active thread
* Leaving a thread mid-turn is safe — the work continues server-side and the thread re-attaches to it when you come back
* Chats from before threads existed live on in a single adopted thread
### Modes
The chat has three modes. The mode pill sits above the message box, and the tooltip summarises them:
> *"Build writes code + glossary; Plan updates PLAN.md; Ask is read-only Q\&A"*
| Mode | What it does | Reach for it when |
| - | - | - |
| **Build** | Writes queries and code, then compiles to check its work. The default. | You want something built or changed. |
| **Plan** | Explores your data and writes a plan into `PLAN.md`. Touches no application code. | The datasite is substantial and you want to agree the shape first. |
| **Ask** | Read-only. Answers questions about the datasite and its data. | You want to understand what's there without changing anything. |
### The Plan → Build handoff
When a Plan turn finishes, a **Build this Plan** button appears on the message. Clicking it hands the plan straight to a Build turn. The editor also switches to the Code tab with `PLAN.md` open so you can read — and edit — the plan before committing to it.
`PLAN.md` renders as formatted text with an **Edit as text** link if you'd rather rewrite it by hand.
### While it works
Assistant messages show a **Thought for N steps** toggle that expands into what it actually did — every query it ran, every file it wrote. Worth opening when a result surprises you.
Turns keep running even if you close the tab; the work continues server-side and shows up in the history when it's done.
The send button becomes a **Stop** button while a turn is running. Stopping is safe — anything already finished, like a saved query or a written file, is kept.
## The Glossary
The **Glossary** is the datasite's saved-query layer. This is the concept most worth understanding, because it's what keeps a datasite honest.
> Despite the name, it is not a dictionary of business terms. Each glossary item is a **named, saved query** with an ID.
Here's the arrangement: the analyst writes a query, tests it against your database, and saves it under an ID like `revenue-by-region`. The generated page then asks for data *by that ID*. The query itself never reaches the browser — visitors to your published datasite get results, never SQL, and never credentials.
### Working with glossary items
Open the **Glossary** tab. Saved queries are listed on the left; select one to see its title, its SQL, and a **Save & Run** button that executes it and shows the result table.
Every save re-runs the query first. A query that errors won't save, so a broken glossary item can't silently ship.
### Edit with AI
The **Edit with AI** button stages that one query for the chat — a chip appears above the message box showing which item you're editing. Describe the change and send.
Scoping the edit this way matters: it constrains the analyst to that item and the components bound to it. The item keeps its ID, so every reference in the page keeps working, and the rest of your datasite is left alone.
### Filters
Glossary items can declare which columns are filterable, which is what powers the filter controls on the page.
> **A filter only works if its query actually reads the table it's declared against.** If the analyst declares a filter on a column from a table the query never joins, the filter is silently ignored — the page returns unfiltered data and the control looks broken.
If a filter isn't doing anything, that's the first thing to check. Telling the analyst *"the region filter isn't affecting the chart"* is usually enough for it to rewrite the query to join the table it needs.
## Pinning Existing Widgets In
You don't have to rebuild work that already exists. Any chart, table, or KPI can be pinned into a datasite.
From a widget's **Pin widget** popup — or the pin menu in [the Analyst](/analyst/overview) — choose the **Datasites** tab and pick a datasite. You're taken to that datasite with the widgets waiting as chips above the message box.
They ride along with your next message, so you can say where you want them: *"add these to a new section at the bottom, under a heading called Pipeline Health"*. Send an empty message and they're simply added.
> **The pinned query is used exactly as written.** It isn't rewritten, reformatted, or merged with anything — so the numbers on your datasite match the numbers you pinned, to the row. The widget keeps its type too: a line chart stays a line chart. Only the styling is rebuilt so it looks native to the datasite.
If the pinned query errors when it runs, the analyst reports the error rather than "fixing" it — the query is yours to change.
## The Code Tab
Everything the analyst writes is readable and editable.
**The file tree** has two sections:
* **Source** — the files you can edit. Components live under `components/`, one per file; configuration and styling under `lib/`. Charts are built with recharts.
* **Infra · read-only** — four files (`index.html`, `main.jsx`, `sb/client.js`, `sb/params.js`) that wire the site to your data. These are injected at build time and can't be edited.
Select a file to open it in the editor. **Copy** copies its contents; **Save** writes the file and rebuilds the preview.
> Saving rebuilds the **preview**, not the live site. Your published datasite is unaffected until you publish again.
There's no autosave and no diff view of the analyst's changes, so save deliberately — and if you're making a large hand edit, copy the original somewhere first.
## Select & Edit Elements
The most direct way to change something you can see.
Click **Select & edit elements** in the preview toolbar. A strip appears — *"Select mode on — click any element to edit it"* — and the cursor becomes a crosshair. Click anything in the preview.
Select mode switches itself off and a chip appears above the message box naming what you picked. Your next message is scoped to that element: *"make this number bigger and put the change percentage underneath it"*.
Much faster than describing which of five KPI tiles you mean.
## When a Build Fails
If the analyst writes code that doesn't compile, the preview is replaced with:
> **Build failed · preview not updated**
> Fix the errors below — your live embed still serves the last published build.
Each error names its file, line, and the offending code. The **Fix with AI** button hands all of it back to the analyst, which is almost always the right move.
A green build that then crashes at runtime shows a red strip with the error and the same **Fix with AI** button.
> A failed build never takes your published datasite down. Visitors keep seeing the last version you published.
## Environments
An **environment** remaps which data source a datasite queries, without touching its code or its queries. The usual case is pointing the same datasite at staging and at production.
Open the environment picker in the preview toolbar and choose **Manage environments**.
| Field | Notes |
| - | - |
| **Name** | e.g. `Production` |
| **Set as default environment** | Used when no environment is specified |
| **Resource Mapping** | Rows of *Original Resource* → *In This Environment* |
Resources this datasite actually queries are sorted to the top of the pickers.
**Selecting one:** the preview picker switches the preview, and adding `?environment=` to a datasite URL switches it for a viewer. That URL parameter overrides whatever the link would otherwise use.
Switching environments needs no rebuild — the same site simply queries elsewhere.
Every datasite starts with an environment named `default` that maps nothing, so it queries your original sources until you say otherwise. The default environment can't be deleted, since links that don't specify one fall back to it.
## Publishing and Embedding
### Publish
The **Publish** button is disabled until at least one preview build has succeeded — you can't publish a datasite that doesn't compile.
Clicking it opens a popover showing the datasite's address and its visibility. Publish, and the popover switches to showing the live version with a copyable URL. **Update** republishes your current preview build over the live one.
First publish also mints the datasite's own address — a subdomain named after the datasite, like `revenue-overview-x7k2p.supaboard.live` — which is what embed links use. You can put a domain you own in front of it from the same popover. Both are covered in [Datasite Custom Domains](/datasites/datasite-custom-domains).
### Public or private
The pill next to the address toggles visibility:
| Setting | Tooltip |
| - | - |
| **Public** | *"Anyone with the link can view. Click to make private."* |
| **Private** | Viewers sign in, and only people with access can open the site. |
Datasites are **private** unless you make them public.
### Sharing the link
This is the part worth getting right, because the two visibilities behave differently:
* **Public** — the copied URL includes an access token, and needs it. The link works for anyone, with no sign-in. Copy the URL exactly as given; a trimmed link won't load data.
* **Private** — the copied URL has no token. Viewers are asked to sign in — with email and password, or via **Continue with Google** / **Continue with Microsoft** — and only people with access to the Datasite can get in. The OAuth buttons don't create accounts; an email your workspace doesn't know is turned away.
Embed a datasite by putting its URL in an `