SET CHANGE_TRACKING = TRUE;` if it is not already on.
* **At least one text field** containing the content you want to search
* **A unique identifier field** (primary key) on each record
For automation triggers like "Record is updated" on Snowflake tables, change tracking must be enabled **before** configuring the trigger.
## How It Works
1. **Content processing** — Your text data is converted into high-dimensional vectors (embeddings) that represent semantic meaning.
2. **Query understanding** — When you search, your query is converted into an embedding using the same model.
3. **Similarity matching** — The system finds content whose embeddings are closest to your query, regardless of exact word overlap.
4. **Results ranking** — Results are ordered by semantic similarity so the most relevant records appear first.
## Setting Up AI Search
There are two ways to set up AI Search: create a new search table from scratch, or link an existing Cortex Search service that already lives in Snowflake.
### Create a New Search Table
In the Intelligence tab, select **AI Search** from the menu.
Click **+ Search Table** to begin configuring a new searchable table.
* **Standard Table** — Select a Standard Snowflake table
* **Unique Identifier** — Specify the field that uniquely identifies each record
* **Fields to Search** — Add one or more text fields to search. For each field, choose a search type:
* **Semantic search** — Finds results by meaning and context using embeddings, even when wording differs from the query
* **Keyword search** — Matches results based on exact terms in the query
* **Attribute Fields** — Select additional fields to return alongside search results (e.g., category, status, date)
* **Service** — Choose your configured Snowflake Cortex embedding service
* **Archival Service** — Select the Snowflake Cortex service to use when running search queries
* **Target Lag** — Set how frequently the search index updates (`1` = daily)
Click **Create** to build the search index. Initial indexing may take time depending on data volume.
After clicking **Create**, track the configuration status on the AI Search page. The table displays a status indicator that moves from **Loading** to **Ready** once the search index is fully built and available for queries. If an error occurs during creation, click **Retry** to reattempt the search table setup.
### Link an Existing Cortex Search Service
If you already have a Cortex Search service running in Snowflake, you can link it directly to an App or Element without recreating it in Elementum. The linked service becomes available for AI Search queries immediately.
Open the App or Element where you want to use the existing search service. In the Intelligence tab, select **AI Search** from the menu.
Click **+ Search Table** and select the option to link an existing Cortex Search service.
Provide the following information to locate the service in Snowflake:
* **Provider** — Select the Snowflake Cortex provider configured in your organization
* **Database** — Choose the Snowflake database where the service resides
* **Schema** — Select the schema that contains the Cortex Search service
* **Search Table Name** — Enter the name of the existing Cortex Search service
Click **Create** to link the service. Once linked, the search table is available for AI Search queries on the App or Element.
Removing a linked Cortex Search service from Elementum only removes the reference — it does not delete the underlying service in Snowflake. Your Cortex Search service continues to run independently in your Snowflake environment.
#### What you can configure on a linked service
When you link an existing Cortex Search service, Elementum only stores the pointer to the service (provider, database, schema, and service name). Configuration of the service itself lives in Snowflake.
**Managed in Elementum:**
* Referencing the linked service in AI Search queries on the App or Element
* Using the service in Automations (via the [AI Data Search](/workflows/automation-actions-reference#ai-data-search) action) and as an Agent tool
* Removing the link (which does not affect the underlying Snowflake service)
**Managed in Snowflake, not in Elementum:**
* Search fields and search types (semantic vs. keyword)
* Attribute fields returned with results
* Embedding service and index refresh cadence (Target Lag)
* Scoring profiles and any other Cortex Search service properties
To change any of these on a linked service, update the service definition in Snowflake. To manage these from Elementum, create a new search table with **Standard Table** instead of linking (see above).
## Using the Search Interface
Once a search table is configured, you can query it directly from the AI Search interface.
**Natural language queries** — Type questions in plain language. AI Search understands meaning, so queries like "issues with payment processing" or "customer complaints about delivery" return semantically relevant results even when records use different phrasing.
**Filtering** — Combine semantic search with attribute filters to narrow results by field values, date ranges, or categories.
**Results** — Each result includes the matched text, a similarity score indicating how closely it relates to your query, and any attribute fields you configured during setup.
## AI Search in Other Features
AI Search integrates with automations, agents, and Tables. Rather than configuring AI Search differently in each context, you set it up once (as described above) and then reference the search table where needed.
### Automations
Use the **AI Search** action in an automation to run a semantic query as part of a workflow. Select a configured AI Search table as the source and pass a dynamic query using value references from earlier in the automation (e.g., `{{trigger.description}}`).
For full configuration details and output variables, see [AI Data Search](/workflows/automation-actions-reference#ai-data-search) in the Automation Actions Reference. For broader guidance on using AI within automations, see [AI in Automations](/ai-agents/ai-automations).
### Agents
AI Search can be configured as a tool that agents call at runtime to look up relevant records during a conversation. When adding AI Search as an agent tool, you can define input value references with descriptions and use them in filters so the agent passes values dynamically for context-aware search.
For details on configuring agent tools, see [Agent Skills](/ai-agents/agents-skills). For building and managing agents, see [Building Agents](/ai-agents/agents-experience).
### Tables
Table admins can enable AI Search on any Table. The setup follows the same process described above. For details on enabling AI Search within the Tables interface, see the [AI Search section](/data/tables#ai-search) on the Tables page.
## Tips for Better Results
* **Choose descriptive text fields.** The fields you index should contain meaningful, readable content — not codes or IDs. Fields with complete sentences or descriptions produce better semantic matches.
* **Keep content at a reasonable length.** Embedding models work best with content in the 512–1,024 token range. Very short or very long text can reduce match quality.
* **Use consistent language.** Consistent terminology and formatting across records improves how well the embedding model captures your domain's semantics.
* **Set appropriate result limits.** For most use cases, 10–50 results strikes the right balance between coverage and relevance. In automation workflows, start with a smaller limit and increase if needed.
* **Update your index regularly.** The Target Lag setting controls how often new or changed records are indexed. Daily updates (Target Lag = `1`) work well for most cases; adjust if your data changes more frequently.
## Troubleshooting
**Symptoms:** AI Search fails to create or index properly, or you receive errors about change tracking.
**Solutions:**
1. Enable change tracking on the source Snowflake table:
```sql theme={null}
ALTER TABLE SET CHANGE_TRACKING = TRUE;
```
2. Verify change tracking is active:
```sql theme={null}
SHOW TABLES LIKE '';
```
Check the `change_tracking` column in the output.
3. For "Record is updated" automation triggers, change tracking must be enabled before configuring the trigger.
**Symptoms:** The setup dialog does not allow you to create a search configuration.
**Solutions:**
1. Confirm the table is a **Standard Snowflake table**. Transient, temporary, hybrid, external, and dynamic tables are not supported.
2. Verify that a Snowflake Cortex **embedding service** is configured under [AI Services](/ai-agents/ai-services).
3. Check that change tracking is enabled on the underlying table.
4. Ensure you have the necessary permissions to configure search.
**Symptoms:** Searches return results that don't match the intent of your query.
**Solutions:**
1. Review the content in your indexed text fields — short, vague, or code-like values produce weaker embeddings.
2. Try rephrasing your query to be more specific.
3. Check that the correct embedding service is selected in the search configuration.
4. If the indexed content has changed significantly since the last index update, wait for the next index refresh or reduce the Target Lag setting.
**Symptoms:** Search queries take longer than expected to return results.
**Solutions:**
1. Reduce the number of attribute fields returned with results.
2. Lower the result limit if it is set high.
3. Ensure your Snowflake warehouse is appropriately sized for the data volume.
4. Check Snowflake Cortex service status for any provider-side latency.
## Next Steps
Create and manage the embedding services that power AI Search
Learn how AI actions — including AI Search — fit into automation workflows
Create agents that use AI Search as a runtime tool
Full configuration details for the AI Search automation action
# AI Services
Source: https://docs.elementum.io/ai-agents/ai-services
Create and manage AI services for LLMs and embeddings using your configured providers
## What Are AI Services?
AI Services are specific AI model instances that you configure for use in your workflows. While AI Providers establish connections to external AI platforms, AI Services define the actual models, settings, and configurations that power your AI features.
**Prerequisites**: You must have at least one AI Provider configured before creating AI Services. See the [AI Overview](/ai-agents/ai-overview#ai-providers) for setup instructions.
## Types of AI Services
Elementum supports two types of AI Services:
* **LLM Services** -- Language models for text generation, conversation, and analysis. Used for [agents](/ai-agents/agents-experience), [automation actions](/ai-agents/ai-automations), and content generation.
* **Embedding Services** -- Embedding models for semantic search and similarity analysis. Used in [AI Search](/ai-agents/ai-search) to convert data into vector representations for semantic querying.
## Prerequisites
Before creating services, you need at least one configured AI provider. Provider setup is covered on its own page per provider; once your provider is saved in **Organization Settings → Providers**, return here to create services.
Connect OpenAI as a provider
Connect Anthropic for direct Claude access
Connect Snowflake Cortex for LLM and embedding services
Connect Vertex AI Gemini
Connect Bedrock-hosted models
Connect any OpenAI-compatible endpoint (configured below)
**Only verified domain users can configure AI Providers.** Elementum employees cannot create or modify AI Providers or Models in any customer org. See the [AI FAQ](/support/faq/faq-ai) for compliance details.
**CloudLink type matters.** AI services are not supported on [API CloudLinks](/administration/connect-rest-api-cloudlink). They require a data-warehouse CloudLink such as a [Snowflake CloudLink](/administration/connect-snowflake-to-elementum) (with key-pair authentication for Cortex features).
## Configure a Custom Provider
Use the **Custom** provider type to connect any OpenAI-compatible endpoint, including LLM gateways, proxies, and self-hosted models. Once configured, a custom provider can be used across agents and automations just like any built-in provider.
1. In **AI Services**, click **+ Connect Provider** and choose **Custom**
2. Enter a **Name** to identify the provider in Elementum
3. Enter the **URL** of the OpenAI-compatible endpoint
4. Select the connection type from the dropdown:
* **API Key** -- Provide a static API key issued by your endpoint
* **OAuth Credentials** -- Provide the OAuth Client Credentials (client ID, client secret, and token URL) used to obtain a bearer token
5. Enter the required credentials for the connection type you selected
6. Click **Save**
Once the custom provider is configured, click **+ Add Models** on the provider to add any model identifier exposed by the endpoint. Custom-provider models then appear in the model dropdown when creating an LLM Service.
The endpoint must implement the OpenAI Chat Completions API contract. Capabilities available to a custom-provider model (such as structured output, multimodal input, or reasoning) depend on what the underlying endpoint supports.
## Manage Providers
Once a provider is connected, click on it in the **Providers** tab to:
* View connection details and status
* Edit the provider configuration or credentials
* Delete the provider
* Click **+ Add Models** to make additional models from this provider available to your services
## Configure Provider Failover
Configure one or more backup providers so traffic automatically reroutes if the primary provider is unreachable. No user action is required during an outage.
**Prerequisites:**
* At least two configured AI Providers of compatible model families.
* Each provider must have active, tested credentials.
**Configuration steps:**
1. On the **Providers** tab, open the dropdown for the desired provider and click the
**Provider Details** icon.
2. Click the
**Edit** icon next to **Backup Providers**.
3. Select a provider to use in case of failover. Choose multiple to ensure several options are available.
4. Click **Save**.
**Behavior notes:**
* Failover is automatic — no user action is required during an outage.
* Only providers whose status is **Active** are eligible targets.
* Failover applies to all features consuming the primary provider (agents, search, summarization, and so on).
* When the primary provider recovers, new requests resume routing to it.
## Migrate a Model Across the Organization
Replace any AI model with a different model in a single action. Every automation action and agent that references the source model switches over automatically, so you don't need to update each one individually.
**Configuration steps:**
1. On the **Providers** tab, click the dropdown next to the service whose model you want to replace.
2. Click the
**Replace Model** icon.
3. Select the new model from the dropdown.
The popup lists every automation that uses the current model so you can review the impact before confirming.
4. Click **Replace**.
**Behavior notes:**
* The migration runs in the background and can take time when many automations reference the source model. Track progress under **Background tasks**.
* Agents and automation actions referencing the source model are updated in place — you don't need to reopen and republish each one.
## Create AI Services
To create a service:
1. Navigate to the **AI Services** page and open the **Services** tab.
2. Click **+ Service** and choose the service type—**LLM** for language models or **Embedding** for AI Search.
3. Configure the fields described below for the service type you selected.
4. Click **Save**. New services appear in the **Services** tab and can be [tested](#test-services) before assignment.
### Create an LLM Service
LLM Services power conversational AI, text generation, and intelligent automation.
**Service Name**: Give your service a descriptive name (e.g., "Customer Support Bot")
**Provider**: Select your configured AI Provider
**Model**: Choose from available models for your provider. See [AI Models](/ai-agents/ai-models) for a detailed comparison of capabilities, use cases, and pricing considerations across all providers.
**Cost Per Million Tokens**: Optional cost tracking (varies by provider)
**Temperature**: Controls randomness and creativity of responses (0.0–1.0). Lower values produce more deterministic, consistent outputs; higher values produce more varied, creative responses. Set to 0.0–0.3 for automation tasks like classification where consistency matters.
**Reasoning Effort**: Controls how much computational effort the model invests in internal reasoning before responding (minimal/low/medium/high)
* Use **minimal** for simple lookups and straightforward answers
* Use **low** for basic reasoning tasks and simple problem solving
* Use **medium** for moderate analytical tasks requiring multi-step reasoning (default)
* Use **high** for complex problem solving, mathematical proofs, multi-step logic, and detailed analysis
**Max Tokens**: Maximum response length
**Top P**: Controls diversity of responses (nucleus sampling)
**Frequency Penalty**: Reduces repetition in responses
**Presence Penalty**: Encourages topic diversity
**Stop Sequences**: Custom stop sequences for response control
For use-case-specific tuning recommendations (e.g., optimal temperature for classification vs. content generation), see the [Temperature Settings](/ai-agents/ai-models#temperature-settings) and [Performance Optimization](/ai-agents/ai-models#performance-optimization) sections on the AI Models page.
### Create an Embedding Service
Embedding Services enable [AI Search](/ai-agents/ai-search) and semantic understanding.
**Service Name**: Descriptive name (e.g., "Document Search Embeddings")
**Provider**: Select your configured AI Provider
**Model**: Choose from available embedding models:
* **Snowflake Arctic L V2.0** -- Latest high-quality embeddings
* **Snowflake Arctic M V1.5** -- Reliable embeddings for production use
**Dimensions**: Embedding vector size (varies by model)
**Batch Size**: Number of texts to process simultaneously
**Chunk Size**: Maximum text length per embedding
**Overlap**: Text overlap between chunks (for long documents)
**Normalization**: Whether to normalize embedding vectors
**Encoding**: Text encoding method (usually UTF-8)
## Assign to Features
Assign AI models to Elementum features at the organization level so those features have a default model available across your workflows.
1. Navigate to the **AI Services** page and click the **Features** tab
2. Click **+ Assign Model** next to a feature and select a model from the dropdown
3. Click **Save**
To update an existing assignment, click **Change** next to the currently assigned model, select a new model from the dropdown, and click **Save**.
The dropdown for each feature only lists models that Elementum has enabled for that feature. This is why the model list can differ between features -- for example, between **Transform Data with AI** and **AI Classification** -- even when both features use models from the same providers. Enablement is managed by Elementum's engineering team based on how well each model fits the feature's task; it is not something admins configure per model or per service. For more detail on this behavior in automation actions, see [Model Availability Across AI Actions](/ai-agents/ai-automations#model-availability-across-ai-actions).
***
## Test Services
Before using AI Services in production, test them from the Services list:
1. Click on a service name to open the testing interface
2. For **LLM Services**: enter sample prompts, review AI-generated responses, adjust parameters, and monitor response times
3. For **Embedding Services**: enter sample text, review generated embedding vectors, and test similarity calculations between texts
## Manage and Optimize
Once your services are created and tested, keep the following in mind:
* **Model selection** -- The right model depends on your use case. For recommendations by task type (agents, classification, content generation, semantic search) and guidance on balancing cost and performance, see the [Model Selection Guide](/ai-agents/ai-models#model-selection-guide).
* **Cost optimization** -- Right-size your model choices, write concise prompts, and set appropriate token limits to control spending. See [Cost Optimization](/ai-agents/ai-models#cost-optimization) for detailed strategies.
* **Multiple providers** -- You can configure services across different providers for redundancy or to use different model strengths for different tasks. See [AI Providers](/ai-agents/ai-overview#ai-providers) for setup details.
* **Feature-specific guidance** -- For details on how AI Services integrate with specific capabilities, see [AI in Automations](/ai-agents/ai-automations) for automation actions, [Building Agents](/ai-agents/agents-experience) for conversational agents, and [AI Search](/ai-agents/ai-search) for embedding-powered search.
## Troubleshooting
**Symptoms:** Cannot create new AI services
**Common Causes:**
* AI Provider not configured
* Invalid model selection
* Insufficient permissions
**Solutions:**
1. Verify AI Provider is properly configured
2. Check model availability for your provider
3. Ensure proper permissions are granted
4. Try creating with different model options
**Symptoms:** Slow response times or quality issues
**Common Causes:**
* Inappropriate model selection
* Suboptimal configuration
* Network or provider issues
**Solutions:**
1. Review model selection for your use case
2. Optimize service configuration settings
3. Check provider status and network connectivity
4. Consider switching to different models
**Symptoms:** Unexpected high token usage or costs
**Common Causes:**
* Inefficient prompts or queries
* Inappropriate model selection
* Excessive API calls
**Solutions:**
1. Review and optimize prompts
2. Use more cost-effective models where appropriate
3. Implement caching and batching
4. Monitor and analyze usage patterns
## Next Steps
Compare models across providers to choose the right one for your use case
Use embedding services to power semantic search across your data
Create conversational AI assistants using your LLM services
Add AI-driven actions to your automation workflows
# Anthropic Setup
Source: https://docs.elementum.io/ai-agents/anthropic-setup
Configure Anthropic as your AI provider for direct access to Claude models in agents and automations
## Overview
Anthropic is an AI provider in Elementum that connects directly to the Anthropic API, giving you access to Claude models for agents, automation actions, and other AI-driven features. Using Anthropic as a direct provider expands model choice in your organization without routing requests through Snowflake Cortex or AWS Bedrock.
Anthropic Claude is also the primary model used by [Studio Agents](/ai-agents/studio-agents), Elementum's coding-based agents that generate automations, agents, and flows through conversation.
**Prerequisites**: You'll need an Anthropic account with API access. Workspace and organization accounts on the Anthropic Console are both supported.
## Step 1: Get Your Anthropic API Key
### Create an Anthropic Account
1. **Visit the Anthropic Console**
* Go to [console.anthropic.com](https://console.anthropic.com)
* Sign up for an account or log in to your existing account
2. **Set Up Billing**
* Navigate to **Settings** → **Billing**
* Add a payment method to enable API access
* Consider setting usage limits and budget alerts to control costs
### Generate Your API Key
In the Anthropic Console, navigate to **Settings** → **API Keys**
Click **Create Key**
Give your key a descriptive name like "Elementum Integration"
**Critical**: Copy the API key immediately and store it securely
You won't be able to view the full key again after closing the dialog
If you're using Workspaces, scope the key to the workspace whose usage limits and billing should apply to Elementum traffic
Ensure the key has access to the Claude models you plan to use
Never share your API key or commit it to version control. Store it in a secure location like a password manager.
## Step 2: Configure Anthropic in Elementum
### Add the Provider
1. In Elementum, go to **Organization Settings** and select the **Providers** tab
2. Click **+ Provider** and select **Anthropic** from the provider options
3. Configure the provider settings:
**Provider Name**: Enter a descriptive name (e.g., "Anthropic Production")
**API Key**: Paste your Anthropic API key
**CloudLink**: Select which CloudLinks can access models from this provider. Leave as "All CloudLinks" unless you need to restrict access.
AI services do not support CloudLinks configured with **API** as the connection type. Only CloudLinks connected to a supported data warehouse (Snowflake, BigQuery, or Databricks) can be associated with this provider.
**Request Timeout**: Default is usually sufficient (30 seconds)
**Max Retries**: Number of retry attempts for failed requests (default: 3)
**Rate Limiting**: Anthropic handles rate limiting automatically based on your account tier
4. Click **Save** to create the provider. Elementum will automatically validate your API key — look for a green checkmark indicating a successful connection.
## Step 3: Create your first AI service
With your Anthropic provider configured, create an AI Service that uses a Claude model. See [AI Services](/ai-agents/ai-services) for the full walkthrough, including LLM service configuration, assignment, and failover.
For Claude model capabilities and recommended use cases, see [AI Models](/ai-agents/ai-models#anthropic-direct).
**Studio Agents**: Anthropic is the primary provider supported on Studio Agents. When using Studio Agents to build automations, agents, and flows, select a Claude model configured through this provider.
**Note**: Embeddings for AI Search are handled exclusively through Snowflake Cortex. Anthropic models are used for LLM services only.
## Usage Guidelines
### Cost Management
Anthropic charges based on input and output token usage, with rates that vary by model. To manage costs:
* Monitor usage in the Anthropic Console
* Set up budget alerts and spend limits
* Review token consumption by model regularly
* Use Haiku models for high-volume, lower-complexity tasks
* Reserve Opus models for tasks that require the highest reasoning quality
* Right-size prompts and set appropriate max-token limits
* Cache or reuse system prompts where possible
### Best Practices
* Use **Claude Haiku** models for fast, high-volume operations and simple automations
* Use **Claude Sonnet** models for balanced reasoning, production agents, and detailed analysis
* Use **Claude Opus** models for the most demanding reasoning and content tasks
* Be specific and clear in your prompts
* Use system messages to set consistent behavior and tone
* Provide examples for tasks that need a particular format
* Break complex requests into structured, step-by-step instructions
* Choose Haiku models for speed-critical applications
* Use Sonnet for the best balance of quality, speed, and cost
* Reserve Opus for tasks where quality matters more than latency or cost
* Implement retry logic with exponential backoff for transient errors
## Troubleshooting
**Symptoms:** API key rejected or unauthorized errors
**Common Causes:**
* Invalid or revoked API key
* Insufficient workspace permissions
* Billing issues on the Anthropic account
**Solutions:**
1. Verify the API key is correct and active in the Anthropic Console
2. Check the workspace the key is scoped to
3. Confirm billing is active and payment methods are valid
4. Regenerate the API key if needed and update the provider in Elementum
**Symptoms:** Desired Claude model doesn't appear in service creation
**Common Causes:**
* Your Anthropic account or workspace doesn't have access to the model
* Regional restrictions
* Model deprecation or rollout in progress
**Solutions:**
1. Confirm model availability for your account in the Anthropic Console
2. Review workspace-level access to the model
3. Contact Anthropic support for access questions
4. Consider an alternative Claude model with similar capabilities
**Symptoms:** Requests being throttled or rejected
**Common Causes:**
* Exceeding account-tier rate limits
* High concurrent usage across agents and automations
* Burst traffic on a single workspace
**Solutions:**
1. Implement exponential backoff and retries
2. Spread traffic across less time-sensitive workflows
3. Request a rate-limit increase from Anthropic
4. Use multiple workspaces or keys for traffic segmentation
## Security Considerations
* Never expose API keys in client-side code or shared documents
* Rotate keys regularly
* Scope keys to the narrowest workspace that meets your needs
* Monitor key usage for anomalies in the Anthropic Console
* Review Anthropic's current data handling and privacy policies
* Consider data sensitivity when crafting prompts
* Implement data sanitization for fields that may contain PII or secrets
## Next Steps
With Anthropic configured as your AI Provider:
Set up specific LLM services that use your Claude models
Compare Claude models and pick the right one for your use case
Create conversational AI assistants using Claude models
Add AI capabilities to your automation workflows
# AWS Bedrock Agents Setup
Source: https://docs.elementum.io/ai-agents/bedrock-agents-setup
Connect a Bedrock Agent built in AWS to an Elementum App via App Intelligence, and invoke it from automations
## Overview
AWS Bedrock Agents are agents you build in your own AWS account using Amazon foundation models, knowledge bases, action groups, and guardrails. Elementum lets you connect a Bedrock Agent to an App through **App Intelligence** so you can invoke it from automations and conversational workflows—while data access and execution stay inside your AWS environment.
You will:
* (If needed) Create a Bedrock Agent and an agent alias in AWS.
* Add `bedrock:InvokeAgent` to the IAM user or IAM role used by your Bedrock AI Provider.
* Connect the agent to an App through App Intelligence using the **Agent Alias ARN**.
* Use the agent in automations via **Run Agent Task**.
**Time required**: About 15–30 minutes if your Bedrock Agent already exists in AWS; longer if you are building the agent from scratch.
## Prerequisites
### Elementum requirements
* **App access**: Access to the App where you want to use the Bedrock Agent.
* **Bedrock AI Provider configured**: An Amazon Bedrock AI Provider must already exist in **Organization Settings → Providers** (using either credential-based or IAM role authentication). If you haven't set this up, complete [AWS Bedrock Setup](/ai-agents/bedrock-setup) first—this guide assumes that's done.
### AWS requirements
Your AWS environment must have:
* An **AWS Account** with Bedrock enabled in your target region.
* **Foundation model access** granted to the model your agent will use.
* The IAM user or IAM role used by your Bedrock AI Provider must include the `bedrock:InvokeAgent` permission (see [Step 2](#step-2-grant-invokeagent-permission)).
**Model access**: Confirm model access in the Amazon Bedrock console for your account and region; approval timing depends on AWS.
## Step 1: Configure the Bedrock Agent in AWS
If you have not yet built the agent in AWS, create it and an agent alias before connecting to Elementum. Skip to [Step 2](#step-2-grant-invokeagent-permission) if you already have a Bedrock Agent and alias.
### Create a Bedrock Agent in AWS
1. Sign in to the AWS Management Console.
2. Navigate to the **Amazon Bedrock** service.
3. Select **Agents** from the left navigation.
Click **Create agent** and configure:
* **Agent name**: Provide a descriptive name (e.g., "Customer Support Agent").
* **Description**: Describe the agent's purpose.
* **Agent resource role**: Create a new role or select an existing one with Bedrock permissions.
Provide clear instructions that define the agent's behavior:
```
You are a helpful customer support assistant. You help users with
their questions about orders, returns, and product information.
Always be polite and professional.
```
Clear, specific instructions lead to better agent performance. Include examples of expected behavior and any constraints.
Choose the foundation model to power your agent.
Model availability depends on your region and account access. Request model access in the Bedrock console if needed.
Optionally enhance your agent with:
**Knowledge Bases:**
* Attach Amazon Bedrock knowledge bases using supported data sources (for example, Amazon S3).
* The agent can retrieve and cite that content when answering.
**Action Groups:**
* Define custom actions via Lambda functions.
* Enable the agent to perform specific tasks.
**Guardrails:**
* Implement content filtering.
* Define topic restrictions.
Click **Create** to save the agent configuration.
The agent will be created in **Draft** status.
### Create an agent alias
Elementum invokes agents with an **Agent Alias ARN**, not the base agent ARN.
1. In the Bedrock console, open the agent and open the **Aliases** tab.
2. Click **Create alias**. Set an alias name and description, and choose **Create a new version and associate it to this alias** so the alias points at a prepared version.
3. After creation, copy the **Agent Alias ARN**. Format: `arn:aws:bedrock:{region}:{account-id}:agent-alias/{agent-id}/{alias-id}`
Example:
```
arn:aws:bedrock:us-east-2:123456789012:agent-alias/ABCD1234EF/GHIJ5678KL
```
### Test the agent in the AWS console
Before connecting to Elementum, verify your agent works correctly:
1. In the Bedrock console, open your agent.
2. Use the **Test** panel on the right side.
3. Send test messages to verify behavior.
4. Confirm responses match your expectations.
## Step 2: Grant InvokeAgent permission
The IAM identity (user or role) backing your Bedrock AI Provider must be able to invoke agents.
* If your provider uses **credential-based authentication**, edit the IAM user's policy.
* If your provider uses **IAM role authentication**, edit the IAM role's policy.
1. In the AWS Console, go to **IAM** and open the user or role used by your Bedrock AI Provider.
2. Edit the attached policy and add `bedrock:InvokeAgent` to the existing statement (alongside `bedrock:InvokeModel`):
```json theme={null}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeAgent"
],
"Resource": "*"
}
]
}
```
**Least privilege**: For production, restrict the `Resource` to specific model and agent ARNs:
```json theme={null}
"Resource": [
"arn:aws:bedrock:us-east-2::foundation-model/*",
"arn:aws:bedrock:us-east-2:123456789012:agent-alias/*"
]
```
3. Save the policy.
If your agent uses knowledge bases or action groups, the agent's own IAM role (not the invoker role) needs additional permissions for those resources.
## Step 3: Connect the agent in App Intelligence
### Open App Intelligence
1. Open the App where you want to use the Bedrock Agent.
2. In the App menu, click **Intelligence**.
### Connect the Bedrock agent
On the Intelligence page, click **+ Connect** (or the control your workspace uses to connect a managed agent).
Choose **Bedrock** as the agent source.
Choose your Amazon Bedrock AI Provider. Only providers with working credentials appear.
Paste the **Agent Alias ARN** from [Create an agent alias](#create-an-agent-alias). Do not use the base agent ARN.
**Agent Name**: Optionally customize the display name in Elementum.
**Description**: Add notes about how this agent will be used in your App.
Click **Save** to connect the external agent.
The agent will now appear in your App's Intelligence configuration.
## Step 4: Test the integration
Verify the agent connection works correctly.
### Test in Elementum
In App Intelligence, click on the connected Bedrock agent.
Click **Chat** to open the interactive testing panel.
Send messages to confirm:
* The agent responds successfully.
* Responses are appropriate and match expectations.
* Latency is acceptable for your use case.
Test various scenarios relevant to your use case:
* Standard queries.
* Edge cases.
* Knowledge base retrieval (if configured).
* Action group execution (if configured).
### Expected behavior
| Test | Expected result |
| ------------------------ | ------------------------------------------- |
| Simple greeting | Agent responds appropriately |
| Domain-specific question | Agent uses knowledge base (if configured) |
| Action request | Agent executes action group (if configured) |
| Out-of-scope question | Agent handles gracefully per instructions |
## Step 5: Use the agent in automations
Use your Bedrock Agent in App automations for production workflows.
### Using agents in automation actions
In the automation builder, the action type is **Run Agent Task**. For full field-level detail, see [Run Agent Task](/workflows/automation-actions-reference) in the automation actions reference.
In your App, open the automation where you want to use the agent.
Add a new action or edit an existing one, then choose **Run Agent Task**.
Under **AI Agent** (or equivalent), choose the external Bedrock Agent you connected in Intelligence.
It may appear as **External** or **Managed**, depending on your workspace.
**Task definition**: Describe what the agent should do and how success is judged. Use value references for record fields, prior action outputs, or static text where supported.
**Output type**: Choose **Text** or **Structured**. For structured output, define fields so later automation steps can map results to records or variables.
Configure any error or follow-up behavior your automation requires after the task completes.
**Timeout**: Set a maximum execution time that fits your agent and knowledge sources (the editor may suggest a default).
**Retry Policy**: Configure retry behavior for transient failures.
**Error Handling**: Define failure behavior:
* Continue with default values.
* Halt automation and alert.
* Escalate to human review.
### Example automation (conceptual)
The following illustrates how steps might flow; exact builder labels can vary by release.
```yaml theme={null}
Workflow: Customer Inquiry Processing
Trigger: New inquiry record created
Automations:
1. Gather Context:
- Collect customer information
- Retrieve previous interactions
2. Run Agent Task – Analysis:
Type: Run Agent Task
Agent: Customer Support Agent (Bedrock / Managed)
Inputs:
- customer_inquiry: {record.description}
- customer_history: {customer.interaction_history}
Outputs:
- response: record.suggested_response
- category: record.inquiry_category
- sentiment: record.customer_sentiment
3. Route Based on Category:
- High priority → Immediate escalation
- Standard → Queue for review
- FAQ → Auto-respond with suggestion
```
## How Bedrock Agent invocation works
When Elementum invokes a Bedrock Agent:
```mermaid theme={null}
sequenceDiagram
participant Auto as Automation
participant Intel as App Intelligence
participant Prov as Bedrock AI Provider
participant AWS as AWS Bedrock
participant BA as Bedrock Agent
participant KB as Knowledge Bases
participant AG as Action Groups
Auto->>Intel: Invoke External Agent
Intel->>Prov: Route via Provider
Prov->>AWS: InvokeAgent API
AWS->>BA: Execute Agent
BA->>KB: Query Knowledge (optional)
KB->>BA: Return Context
BA->>AG: Execute Actions (optional)
AG->>BA: Return Results
BA->>AWS: Generate Response
AWS->>Prov: Return Response
Prov->>Intel: Format Response
Intel->>Auto: Continue Automation
```
### AWS Bedrock API used
**InvokeAgent** sends a prompt to the agent and returns the agent's response (including optional tool and knowledge-base steps on the AWS side).
**Key parameters:**
* `agentAliasId`: The alias ID of the agent.
* `agentId`: The unique identifier of the agent.
* `sessionId`: Session identifier for conversation continuity.
* `inputText`: The message to send to the agent.
**Documentation**: [Amazon Bedrock InvokeAgent API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_InvokeAgent.html)
### Security model
| Aspect | Implementation |
| ------------------- | ---------------------------------------------------------------------------------- |
| **Authentication** | IAM Access Key/Secret Key or IAM Role (assumed at runtime) via Bedrock AI Provider |
| **Authorization** | IAM policies control which agents can be invoked |
| **Data in transit** | TLS encryption for all API calls |
| **Audit** | AWS CloudTrail logs all Bedrock API calls |
| **Isolation** | App-level configuration with provider-based access |
## Monitoring and Maintenance
### Monitoring agent performance
**In Elementum**
* Use automation history and related logs to review invocations, response times, success and failure rates, and error messages.
**In AWS**
* Use CloudWatch and Cost Explorer (as applicable) for Bedrock API volume, latency, errors, and token or usage-related metrics.
### Maintenance tasks
**Weekly:**
* Review automation logs for agent errors.
* Monitor response times and latency.
* Check for timeout patterns.
**Monthly:**
* Review agent usage and costs.
* Audit IAM permissions.
* Test agent behavior after any updates.
**Quarterly:**
* Rotate IAM access keys.
* Review and optimize agent instructions.
* Evaluate new foundation models.
When updating Bedrock Agents:
1. **Test in AWS**: Verify changes in the Bedrock console first.
2. **Create New Alias**: Create a new alias for the updated version.
3. **Update Elementum**: Update the Agent Alias ARN if using a new alias.
4. **Test Integration**: Validate automations with the updated agent.
5. **Monitor**: Watch for issues after deployment.
Use separate aliases for testing and production to safely test changes.
As usage grows:
**AWS Side:**
* Monitor Bedrock service quotas.
* Request quota increases if needed.
* Consider provisioned throughput for consistent performance.
**Elementum Side:**
* Monitor automation execution times.
* Implement caching for repeated queries.
* Use async patterns for long-running operations.
## Troubleshooting
**Error**: "Access Denied" or "Not authorized to perform bedrock:InvokeAgent".
**Possible causes:**
* IAM user or role missing `bedrock:InvokeAgent` permission.
* Policy not attached to the user or role.
* Resource restrictions in policy don't match the agent ARN.
* For IAM Role auth: trust policy does not allow Elementum to assume the role.
**Solutions:**
1. Verify the IAM policy includes `bedrock:InvokeAgent` and is attached to the IAM identity (user or role) configured on the Bedrock AI Provider (see [Step 2](#step-2-grant-invokeagent-permission)).
2. Ensure the policy `Resource` matches your agent alias ARNs or uses a permitted pattern.
3. For credential-based auth, confirm the access keys in Elementum belong to the correct IAM user.
4. For IAM Role auth, verify the role's trust policy allows Elementum to assume it.
**Error**: "Invalid ARN format" or "Resource not found".
**Possible causes:**
* Using the base agent ARN instead of the agent alias ARN.
* Typo in the ARN.
* Wrong region in the ARN.
**Solutions:**
1. Ensure you're using the **Agent Alias ARN**, not the base Agent ARN.
2. Verify the format: `arn:aws:bedrock:{region}:{account}:agent-alias/{agent-id}/{alias-id}`
3. Copy the ARN directly from the AWS console.
4. Check that the region matches your provider configuration.
**Correct format:**
```
arn:aws:bedrock:us-east-2:123456789012:agent-alias/ABCD1234EF/GHIJ5678KL
```
**Incorrect (base agent ARN):**
```
arn:aws:bedrock:us-east-2:123456789012:agent/ABCD1234EF
```
**Error**: "Could not connect to endpoint" or timeout errors.
**Possible causes:**
* Provider configured for a different region than the agent.
* Agent not available in the specified region.
**Solutions:**
1. Verify the region in your Bedrock AI Provider matches where the agent is deployed.
2. Check the region in the Agent Alias ARN.
3. Confirm Bedrock is available in your target region.
4. Update provider configuration if needed.
**Error**: "Agent execution timed out".
**Possible causes:**
* Timeout set too low for agent complexity.
* Agent accessing slow knowledge bases.
* Large response generation.
* Network latency.
**Solutions:**
1. Increase timeout in automation configuration.
2. Optimize agent instructions for faster responses.
3. Review knowledge base configuration for performance.
4. Consider breaking complex tasks into multiple calls.
**Error**: "Alias has no associated version" or unexpected behavior.
**Possible causes:**
* Alias created without linking to a version.
* Agent in draft state without prepared version.
**Solutions:**
1. In the Bedrock console, verify the alias has an associated version.
2. Create a new alias and select "Create a new version and associate it".
3. Ensure the agent is not in draft state.
### Debugging tips
1. **Test in AWS first**: Always verify the agent works in the Bedrock console before troubleshooting Elementum integration.
2. **Check CloudTrail**: Review AWS CloudTrail logs for detailed API call information.
3. **Verify credentials**: Test IAM credentials independently using the AWS CLI.
4. **Review provider status**: Check the Bedrock AI Provider status in Elementum.
## Best Practices
* Use Bedrock Guardrails and clear instruction scope where appropriate.
* Review agent behavior and access periodically.
Clear, concise agent instructions usually produce faster, more predictable responses. Prefer explicit scope, examples, and constraints over long generic prompts.
Track response times in automations and in AWS where you have metrics. Set automation timeouts high enough for knowledge-base retrieval and tool use, without masking real failures.
Where the same or similar agent inputs occur often, consider caching or deduplicating at the automation level so you do not pay latency and usage for identical work.
For automations that invoke agents at high volume, add throttling or batching so you stay within quotas and avoid unnecessary parallel cost spikes.
Review knowledge base size, refresh cadence, and retrieval settings so you are not indexing or retrieving more content than the agent needs.
## Example Use Cases
**Scenario:** Automatically triage and respond to IT support tickets.
**Implementation:**
1. Create a Bedrock Agent with an IT knowledge base (documentation, FAQs).
2. Configure action groups for ticket operations.
3. Connect the agent in the IT Support App Intelligence.
4. Set up automation: New ticket → Agent analysis → Auto-categorize and suggest resolution.
**Outcomes:**
* Faster first response times.
* Consistent ticket categorization.
* Reduced L1 support workload.
**Scenario:** Generate personalized customer communications.
**Implementation:**
1. Create a Bedrock Agent with communication templates and brand guidelines.
2. Configure guardrails for appropriate content.
3. Connect the agent in the CRM App.
4. Automation: Communication request → Agent drafts message → Human review → Send.
**Outcomes:**
* Consistent brand voice.
* Personalized content at scale.
* Faster communication turnaround.
## Next Steps
Configure the Bedrock provider for AI Services (prerequisite for this guide)
Build automations that invoke your Bedrock Agent
Compare native Elementum agents with managed external agents
Reference AWS's official Bedrock Agents documentation
# AWS Bedrock Setup
Source: https://docs.elementum.io/ai-agents/bedrock-setup
Configure AWS Bedrock as an AI provider for Claude and other Bedrock-hosted models in Elementum AI Services
## Overview
This guide walks you through setting up Amazon Bedrock as an AI Provider in Elementum so you can use Bedrock-hosted Claude (and other foundation) models across your AI Services, automations, and agents.
Running models through your own AWS account keeps AI workloads within your cloud infrastructure and compliance boundaries.
**Connecting a Bedrock Agent built in AWS to an Elementum App is a separate setup.** Once this provider is configured, see [AWS Bedrock Agents Setup](/ai-agents/bedrock-agents-setup) to invoke a Bedrock Agent through App Intelligence.
**Time required**: About 15–20 minutes, depending on your existing AWS setup.
## Prerequisites
### Elementum requirements
* **Organization permissions**: Ability to add or edit AI Providers in **Organization Settings**.
### AWS requirements
* **AWS Account**: Active AWS account with Bedrock access.
* **Region**: Bedrock available in your target region (e.g., `us-east-1`, `us-east-2`, `us-west-2`).
* **Bedrock Access**: Amazon Bedrock service enabled for your account.
* **Foundation Model Access**: Access granted to at least one foundation model (Claude, Titan, etc.).
* **IAM Permissions**: Ability to create IAM users and policies.
**Model access**: You need access to the foundation models you plan to use. In the Amazon Bedrock console, confirm model access for your account and region; approval timing depends on AWS.
## Step 1: Prepare AWS Authentication
Elementum supports two methods for authenticating the Bedrock AI Provider with AWS:
* **Credential-based (Access Key + Secret Key)** — Create an IAM user with programmatic access and provide its access keys to Elementum. Best when your organization manages service accounts with long-lived credentials.
* **IAM Role** — Provide an IAM role ARN that Elementum assumes at runtime. Aligns with enterprise AWS security practices by eliminating static credentials and using role-based access instead.
Choose one method below and follow the corresponding steps.
Create an IAM user whose access keys Elementum will use to call Bedrock models.
In the AWS Console, go to **IAM** → **Users** → **Create user**.
* **User name**: Choose a descriptive name (e.g., `elementum-bedrock-invoker`).
* Do not enable console access (programmatic access only).
Create and attach a policy with `bedrock:InvokeModel`. If you also plan to connect Bedrock Agents later, include `bedrock:InvokeAgent` now or add it then.
```json theme={null}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel"
],
"Resource": "*"
}
]
}
```
**Least privilege**: For production, restrict the `Resource` to specific model ARNs:
```json theme={null}
"Resource": [
"arn:aws:bedrock:us-east-2::foundation-model/*"
]
```
1. Open the user details.
2. Go to the **Security credentials** tab.
3. Click **Create access key**.
4. Choose a use case that matches programmatic access from outside AWS, then complete the prompts.
5. Copy and securely store the **Access Key ID** and **Secret Access Key**.
**Store credentials securely**: The secret access key is only shown once. Store it in a secure password manager until you configure it in Elementum.
Create an IAM role that Elementum assumes at runtime to call Bedrock models.
In the AWS Console, go to **IAM** → **Roles** → **Create role**.
Set up the trust relationship so Elementum can assume the role. Select **Custom trust policy** and configure it to allow Elementum's AWS account to assume the role.
Attach a policy with `bedrock:InvokeModel`. If you also plan to connect Bedrock Agents later, include `bedrock:InvokeAgent` now or add it then.
```json theme={null}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel"
],
"Resource": "*"
}
]
}
```
**Least privilege**: For production, restrict the `Resource` to specific model ARNs:
```json theme={null}
"Resource": [
"arn:aws:bedrock:us-east-2::foundation-model/*"
]
```
After creation, copy the **Role ARN** from the role summary page.
Format: `arn:aws:iam::{account-id}:role/{role-name}`
## Step 2: Create the Bedrock AI Provider in Elementum
Configure Elementum to connect to AWS using the authentication method you prepared in Step 1.
1. Go to **Organization Settings** and open the **Providers** tab.
2. Click **+ Provider** and select **Amazon Bedrock**.
3. Enter a **Provider name** and the **Region** where your Bedrock resources are deployed (for example `us-east-2`).
4. Choose your authentication method:
* **Credential-based**: Enter the **Access Key ID** and **Secret Access Key** from your IAM user.
* **IAM Role**: Enter the **Role ARN** from the IAM role you created.
5. Use **Test Connection** to confirm the configuration, then **Save**.
The provider is now available for creating AI Services.
**Tips**
* The provider **Region** must match the region where your Bedrock models are available.
* Use separate providers for different AWS accounts or regions if needed.
* IAM Role authentication avoids static credential rotation and aligns with AWS security best practices for enterprise environments.
## Step 3: Create your first AI service
With the provider saved, create an AI Service that uses a Bedrock-hosted model. See [AI Services](/ai-agents/ai-services) for the full walkthrough, including LLM and embedding service configuration, assignment, and failover.
Bedrock-hosted models run within your AWS account, keeping AI workloads inside your own cloud infrastructure and compliance boundaries.
For a side-by-side comparison of available Bedrock models against other providers, see [AI Models](/ai-agents/ai-models).
## How Bedrock model invocation works
When Elementum invokes a Bedrock-hosted model:
```mermaid theme={null}
sequenceDiagram
participant Auto as Automation / Agent
participant Svc as AI Service
participant Prov as Bedrock AI Provider
participant AWS as AWS Bedrock
participant FM as Foundation Model
Auto->>Svc: Generate completion
Svc->>Prov: Use Bedrock provider
Prov->>AWS: InvokeModel API
AWS->>FM: Run prompt on model
FM->>AWS: Return tokens
AWS->>Prov: Return response
Prov->>Svc: Return response
Svc->>Auto: Continue
```
### AWS Bedrock API used
**InvokeModel** sends a prompt to a Bedrock-hosted foundation model and returns the model response. Used by all AI Services created with the Bedrock provider.
**Key parameters:**
* `modelId`: The identifier of the foundation model.
* `body`: The request payload (prompt, parameters).
* `contentType` / `accept`: Media types for the request and response.
**Documentation**: [Amazon Bedrock InvokeModel API](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_InvokeModel.html)
### Security model
| Aspect | Implementation |
| ------------------- | ---------------------------------------------------------------------------------- |
| **Authentication** | IAM Access Key/Secret Key or IAM Role (assumed at runtime) via Bedrock AI Provider |
| **Authorization** | IAM policies control which models can be invoked |
| **Data in transit** | TLS encryption for all API calls |
| **Audit** | AWS CloudTrail logs all Bedrock API calls |
## Troubleshooting
**Error**: "Access Denied" or "Not authorized to perform bedrock:InvokeModel".
**Possible causes:**
* IAM user or role missing `bedrock:InvokeModel` permission.
* Policy not attached to the user or role.
* Resource restrictions in policy don't match the model ARN.
* For IAM Role auth: trust policy does not allow Elementum to assume the role.
**Solutions:**
1. Verify the IAM policy includes `bedrock:InvokeModel` and is attached to the IAM user (for credential-based auth) or IAM role (for role-based auth) configured on the Bedrock AI Provider.
2. Ensure the policy `Resource` matches your foundation model ARNs or uses a permitted pattern.
3. For credential-based auth, confirm the access keys in Elementum belong to the correct IAM user.
4. For IAM Role auth, verify the role's trust policy allows Elementum to assume it.
**Error**: "Could not connect to endpoint" or timeout errors.
**Possible causes:**
* Provider configured for a different region than where the model is available.
* Model access not granted in the configured region.
**Solutions:**
1. Verify the region in your Bedrock AI Provider matches where the model is enabled.
2. Confirm Bedrock and the model are available in your target region.
3. Update provider configuration if needed.
**Error**: Connection test returns an error despite credentials looking correct.
**Solutions:**
1. Confirm the IAM user or role has at least `bedrock:InvokeModel` permission.
2. Verify the **Region** field uses the AWS region code (for example `us-east-2`, not `US East 2`).
3. Check that no SCP or AWS Organizations policy is blocking Bedrock for the account.
4. For IAM Role auth, confirm the role's trust policy is configured correctly.
## Best Practices
* Apply least privilege; scope `bedrock:InvokeModel` to specific model ARNs when practical.
* Prefer IAM Role authentication for enterprise environments to avoid managing static credentials.
* If using credential-based auth, rotate access keys on a schedule your organization defines (for example, every 90 days).
* Use different IAM users, keys, or roles per environment (development vs production).
Pick a foundation model that balances latency, cost, and quality for your task. Available models depend on your AWS region and account. See [AI Models](/ai-agents/ai-models) for a comparison across providers.
Use AWS Cost Explorer (and related billing views) to monitor token-related usage and Bedrock charges tied to your provider.
Prefer smaller or faster models for straightforward classification or short replies when quality requirements allow; reserve larger models for harder reasoning.
## Next Steps
Create LLM services using Bedrock-hosted models
Compare models across providers
Connect a Bedrock Agent you've built in AWS to an Elementum App
Reference AWS's official Bedrock documentation
# Google Gemini Setup
Source: https://docs.elementum.io/ai-agents/gemini-setup
Configure Google Gemini as your AI provider for language model services
## Overview
Google Gemini provides language models through Google Cloud's Vertex AI platform, including Gemini 2.5 Pro, Gemini 2.5 Flash, and Gemini 1.5 Pro. This guide walks you through setting up Google Gemini as an AI Provider in Elementum.
**Prerequisites**: You'll need a Google Cloud account with billing enabled and access to Vertex AI APIs.
## Step 1: Set Up Google Cloud Project
### Create or Select a Project
1. **Access Google Cloud Console**
* Go to [console.cloud.google.com](https://console.cloud.google.com)
* Sign in with your Google account
2. **Create a New Project** (or select an existing one)
* Click on the project selector at the top of the page
* Click **New Project**
* Enter a project name (e.g., "Elementum AI Integration")
* Select your billing account
* Click **Create**
3. **Enable Billing**
* Ensure your project has billing enabled
* Navigate to **Billing** in the left sidebar
* Link a billing account if not already configured
### Enable Required APIs
Enable the following APIs for Vertex AI access:
1. In the Google Cloud Console, go to **APIs & Services** → **Library**
2. Search for **Vertex AI API** and click **Enable** — this may take a few minutes to complete
3. Search for **Cloud Resource Manager API** and click **Enable** — this is required for project access
## Step 2: Create Service Account
### Generate Service Account
In the Google Cloud Console, go to **IAM & Admin** → **Service Accounts**
Click **Create Service Account**
**Service Account Name**: Enter a descriptive name (e.g., "elementum-ai-service")
**Service Account ID**: Will be auto-generated
**Description**: Optional description for the service account
Assign the following roles to your service account:
**Required Role**:
* **Vertex AI User** (`roles/aiplatform.user`) — Access to Vertex AI models including Gemini
**Optional Roles** (for advanced features):
* **BigQuery User** — If integrating with BigQuery
* **Storage Object Viewer** — If accessing Cloud Storage
Click **Continue** and then **Done** to create the service account
### Generate Service Account Key
In the Service Accounts list, click on your newly created service account
Go to the **Keys** tab
Click **Add Key** → **Create new key**
Choose **JSON** as the key type
Click **Create**
The JSON key file will be automatically downloaded
**Critical**: Copy and store this file securely — it contains credentials for your service account and cannot be downloaded again
Never share your service account key file or commit it to version control. Store it in a secure location like a password manager.
## Step 3: Configure Gemini in Elementum
### Add the Provider
1. In Elementum, go to **Organization Settings** and select the **Providers** tab
2. Click **+ Provider** and select **Gemini** from the provider options
3. Configure the provider settings:
**Provider Name**: Enter a descriptive name (e.g., "Google Gemini Production")
**Location**: Select your Google Cloud region (e.g., "us-central1")
**Project ID**: Enter your Google Cloud project ID
**CloudLink**: Select which CloudLinks can access models from this provider. Leave as "All CloudLinks" unless you need to restrict access.
AI services do not support CloudLinks configured with **API** as the connection type. Only CloudLinks connected to a supported data warehouse (Snowflake, BigQuery, or Databricks) can be associated with this provider.
**Service Account Credentials**: Upload or paste your JSON key file content
The JSON should look like this:
```json theme={null}
{
"type": "service_account",
"project_id": "your-project-id",
"private_key_id": "...",
"private_key": "...",
"client_email": "elementum-ai-service@your-project.iam.gserviceaccount.com",
"client_id": "...",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}
```
4. Click **Save** to create the provider. Elementum will automatically validate your credentials — look for a green checkmark indicating a successful connection.
## Step 4: Create your first AI service
With your Gemini provider configured, create an AI Service that uses a Gemini model. See [AI Services](/ai-agents/ai-services) for the full walkthrough, including LLM service configuration, assignment, and failover.
For a side-by-side comparison of the Gemini models available in Elementum—including recommendations for daily tasks vs. complex reasoning—see [AI Models](/ai-agents/ai-models).
**Note**: Embeddings for AI Search are handled exclusively through Snowflake Cortex. Gemini models are used for LLM services only.
## Usage Guidelines
### Cost Management
Google Cloud charges for Vertex AI usage. To manage costs:
* Monitor usage in the Google Cloud Console
* Set up billing alerts for cost control
* Review and adjust API quotas as needed
* Regularly review usage patterns
* Choose appropriate models for each task
* Use Gemini 2.5 Flash for speed-critical applications
* Cache responses when possible
* Minimize unnecessary API calls
### Best Practices
* Use **Gemini 2.5 Flash** for most general-purpose tasks and customer support
* Use **Gemini 2.5 Pro** for complex reasoning, advanced analysis, and large responses
* Use **Gemini 1.5 Pro** for established production workloads requiring reliable performance
* Be specific and clear in your prompts
* Use system messages for consistent behavior
* Provide examples for better results
* For Gemini 2.5 Pro, structure complex problems step-by-step
* Select Google Cloud regions closest to your users
* Choose Gemini 2.5 Pro for tasks requiring detailed analysis
* Use Gemini 2.5 Flash for high-volume, simple tasks
* Implement retry logic for transient errors
## Troubleshooting
**Symptoms:** Service account authentication failures
**Common Causes:**
* Invalid service account key
* Insufficient permissions
* Disabled APIs
**Solutions:**
1. Verify service account key is valid JSON
2. Check service account roles and permissions
3. Ensure required APIs are enabled
4. Regenerate service account key if needed
**Symptoms:** Cannot access Vertex AI APIs
**Common Causes:**
* APIs not enabled
* Billing not configured
* Regional restrictions
**Solutions:**
1. Enable Vertex AI API in Google Cloud Console
2. Verify billing is enabled and active
3. Check regional availability of services
4. Review project quotas and limits
**Symptoms:** Requests being throttled or rejected
**Common Causes:**
* Exceeding Vertex AI quotas
* High concurrent usage
* Regional quota limitations
**Solutions:**
1. Implement exponential backoff
2. Reduce request frequency
3. Review and adjust quotas in Google Cloud Console
4. Distribute load across multiple regions
**Symptoms:** Expected models don't appear in service creation
**Common Causes:**
* Regional model availability
* Account access restrictions
* Model deprecation
**Solutions:**
1. Check model availability in your region
2. Review account access and permissions
3. Contact Google Cloud support for access issues
4. Consider alternative models
## Security Considerations
* Regularly rotate service account keys
* Use IAM roles for fine-grained access control
* Monitor service account usage for anomalies
* Enable audit logging for security tracking
* Review Google's current data handling and privacy policies
* Consider data sensitivity when crafting prompts
* Data is encrypted in transit and at rest
* Monitor data access patterns
## Advanced Configuration
### Multi-Region Setup
For global deployments, consider the following when selecting regions:
* **Region Selection**: Choose regions closest to your users for lower latency
* **Data Residency**: Ensure your region choices meet data residency requirements
* **Failover**: Implement failover strategies across regions for high availability
* **Compliance**: Verify regional compliance with applicable regulations
### Custom Model Access
If you need access to specialized or private models in Vertex AI:
* **Model Registration**: Register custom models in Vertex AI
* **Access Control**: Configure proper IAM permissions for model access
* **Monitoring**: Set up custom monitoring and alerting for model performance
## Next Steps
With Google Gemini configured as your AI Provider:
Set up specific LLM and embedding services for your workflows
Set up Snowflake Cortex for AI Search and embeddings
Create conversational AI assistants using Gemini models
Add AI capabilities to your automation workflows
# OpenAI Setup
Source: https://docs.elementum.io/ai-agents/openai-setup
Configure OpenAI as your AI provider for language model services
## Overview
OpenAI provides language models including o3, o4-mini, and GPT-4 Omni. This guide walks you through setting up OpenAI as an AI Provider in Elementum.
**Prerequisites**: You'll need an OpenAI account with API access. Individual accounts and organization accounts are both supported.
## Step 1: Get Your OpenAI API Key
### Create an OpenAI Account
1. **Visit OpenAI Platform**
* Go to [platform.openai.com](https://platform.openai.com)
* Sign up for an account or log in to your existing account
2. **Set Up Billing**
* Navigate to **Settings** → **Billing**
* Add a payment method to enable API access
* Consider setting up usage limits to control costs
### Generate Your API Key
In your OpenAI dashboard, navigate to **API Keys** in the left sidebar
Click **Create new secret key**
Give your key a descriptive name like "Elementum Integration"
**Critical**: Copy the API key immediately and store it securely
You won't be able to view it again after closing the dialog
If using an organization account, you can set specific permissions for the key
Ensure the key has access to the models you plan to use
Never share your API key or commit it to version control. Store it in a secure location like a password manager.
## Step 2: Configure OpenAI in Elementum
### Add the Provider
1. In Elementum, go to **Organization Settings** and select the **Providers** tab
2. Click **+ Provider** and select **OpenAI** from the provider options
3. Configure the provider settings:
**Provider Name**: Enter a descriptive name (e.g., "OpenAI Production")
**API Key**: Paste your OpenAI API key
**Endpoint URL** (Optional): Custom endpoint URL for Azure OpenAI or other OpenAI-compatible APIs. Leave blank to use the default OpenAI API endpoint.
**CloudLink**: Select which CloudLinks can access models from this provider. Leave as "All CloudLinks" unless you need to restrict access.
AI services do not support CloudLinks configured with **API** as the connection type. Only CloudLinks connected to a supported data warehouse (Snowflake, BigQuery, or Databricks) can be associated with this provider.
**Request Timeout**: Default is usually sufficient (30 seconds)
**Max Retries**: Number of retry attempts for failed requests (default: 3)
**Rate Limiting**: OpenAI handles rate limiting automatically
4. Click **Save** to create the provider. Elementum will automatically validate your API key — look for a green checkmark indicating a successful connection.
## Step 3: Create your first AI service
With your OpenAI provider configured, create an AI Service that uses an OpenAI model. See [AI Services](/ai-agents/ai-services) for the full walkthrough, including LLM service configuration, assignment, and failover.
For a side-by-side comparison of the OpenAI models available in Elementum—including recommendations for daily tasks vs. complex reasoning—see [AI Models](/ai-agents/ai-models).
**Note**: Embeddings for AI Search are handled exclusively through Snowflake Cortex. OpenAI models are used for LLM services only.
## Usage Guidelines
### Cost Management
OpenAI charges based on token usage. To manage costs:
* Monitor usage in the OpenAI dashboard
* Set up billing alerts
* Review token consumption regularly
* Use appropriate models for each task
* Implement context windowing
* Cache responses when possible
* Use o4-mini for speed-critical applications
### Best Practices
* Use **o4-mini** for most customer support and daily automation tasks
* Use **o3** for complex reasoning, research, and advanced problem-solving
* Use **GPT-4 Omni** for content creation and detailed analysis
* Be specific and clear in your prompts
* Use system messages for consistent behavior
* Provide examples for better results
* For o3, structure complex problems step-by-step
* Choose o4-mini for speed-critical applications
* Use o3 sparingly for tasks requiring maximum intelligence
* Implement caching for repeated queries
* Consider request queuing for high-volume usage
## Troubleshooting
**Symptoms:** API key rejected or unauthorized errors
**Common Causes:**
* Invalid or expired API key
* Insufficient permissions
* Billing issues
**Solutions:**
1. Verify API key is correct and active
2. Check billing status and payment methods
3. Ensure key has proper permissions
4. Regenerate API key if needed
**Symptoms:** Desired model doesn't appear in service creation
**Common Causes:**
* Account doesn't have access to specific models
* Regional restrictions
* Model deprecation
**Solutions:**
1. Check OpenAI account tier and access levels
2. Review model availability in your region
3. Contact OpenAI support for access issues
4. Consider alternative models
**Symptoms:** Requests being throttled or rejected
**Common Causes:**
* Exceeding rate limits
* High concurrent usage
* Account tier limitations
**Solutions:**
1. Implement exponential backoff
2. Reduce request frequency
3. Upgrade account tier if needed
4. Distribute load across multiple keys
## Security Considerations
* Never expose API keys in client-side code
* Rotate keys regularly
* Use environment variables for storage
* Monitor key usage for anomalies
* Review OpenAI's current data handling and privacy policies
* Consider data sensitivity when crafting prompts
* Implement data sanitization if needed
## Advanced Configuration
### Organization Accounts
If you're using an OpenAI organization account, you can centralize API access, billing, and team permissions under a single organization:
* **Organization ID**: Required for organization accounts
* **Member Management**: Control team access through OpenAI dashboard
* **Usage Tracking**: Monitor usage across team members
* **Billing Management**: Centralized billing for the organization
### Custom Endpoints
If you use Azure OpenAI or another OpenAI-compatible API, you can point your provider at a custom endpoint instead of the default OpenAI API:
* **Endpoint URL**: Enter your custom endpoint (e.g., `https://your-resource.openai.azure.com/`)
* **Authentication**: May require additional authentication headers depending on the endpoint
* **Model Names**: Custom model names may be required for non-standard endpoints
* **Rate Limits**: May differ from standard OpenAI limits
## Next Steps
With OpenAI configured as your AI Provider:
Set up specific LLM and embedding services for your workflows
Set up Snowflake Cortex for AI Search and embeddings
Create conversational AI assistants using OpenAI models
Add AI capabilities to your automation workflows
# Automating AI Document OCR with Elementum and Snowflake
Source: https://docs.elementum.io/ai-agents/snowflake-ai-ocr
Extract text from PDFs and images stored in Snowflake stages using AI_PARSE_DOCUMENT OCR capabilities and automated workflows
## Overview
This workflow enables you to automatically extract text content from documents (PDFs, images, etc.) stored in Snowflake stages using Snowflake's AI\_PARSE\_DOCUMENT capability with OCR mode and Elementum's Automation System.
The Snowflake AI OCR workflow consists of nine main steps:
1. **Create a Snowflake stage** for document files
2. **Create an AI OCR stored procedure** in Snowflake
3. **Create a Snowflake view** for stage files
4. **Import the stored procedure** into Elementum via CloudLink
5. **Import the view as an Elementum table**
6. **Build a Data Mine** to monitor for new or changed documents
7. **Create an automation** triggered by the Data Mine
8. **Process documents** using the Run Function action to call your OCR procedure
9. **Add additional actions** to work with the extracted text
This workflow leverages Snowflake AI capabilities to extract text from documents without moving your files outside of your data environment. The OCR processing is orchestrated through Elementum within your Snowflake environment, keeping your data secure and centralized.
## Prerequisites
Before starting this workflow, ensure you have:
* **Snowflake access** with permissions to create stages, views, and stored procedures
* **Elementum CloudLink** configured and connected to your Snowflake instance
* **Documents uploaded** to a Snowflake stage (e.g., PDFs, images)
* **Directory Table enabled on your Snowflake stage** for file listing and metadata access
* **Snowflake AI features enabled** in your account for AI\_PARSE\_DOCUMENT functionality
* **Understanding** of [Elementum Tables](/data/tables), [Data Mining](/data/data-mining), and [Automation System](/workflows/automation-system)
## Step 1: Create Snowflake Stage
First, create a Snowflake stage for your documents with encryption enabled and directory table enabled.
Execute this SQL in your Snowflake environment:
```sql theme={null}
USE DATABASE YOUR_DATABASE;
USE SCHEMA YOUR_SCHEMA;
-- Create internal stage with directory table and encryption enabled
CREATE OR REPLACE STAGE DOCUMENT_STAGE
DIRECTORY = (ENABLE = TRUE)
ENCRYPTION = (TYPE = 'SNOWFLAKE_SSE');
```
## Step 2: Create AI OCR Stored Procedure
Create a stored procedure that uses Snowflake's AI\_PARSE\_DOCUMENT function to extract text from documents.
```sql theme={null}
CREATE OR REPLACE PROCEDURE AI_OCR_FROM_STAGE_SP(FILE_PATH STRING)
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
var sql = `
SELECT TO_VARCHAR(
AI_PARSE_DOCUMENT(
TO_FILE('@YOUR_DATABASE.YOUR_SCHEMA.DOCUMENT_STAGE', ?),
OBJECT_CONSTRUCT('mode', 'OCR')
)
) AS response
`;
var stmt = snowflake.createStatement({
sqlText: sql,
binds: [FILE_PATH]
});
var rs = stmt.execute();
if (rs.next()) {
return rs.getColumnValue(1); // response
} else {
return null;
}
$$;
```
* **`FILE_PATH`**: Takes the relative path of the file within the stage
* **`TO_FILE()`**: References the file in the Snowflake stage
* **`AI_PARSE_DOCUMENT()`**: Snowflake's AI function that processes the document
* **`mode: 'OCR'`**: Specifies OCR mode for text extraction
* **Returns**: JSON string with extracted content and metadata
The response structure looks like this:
```json theme={null}
{
"content": "Extracted text content from the document...",
"metadata": {
"pageCount": 1
}
}
```
Ensure your Elementum CloudLink role has permission to execute the stored procedure:
```sql theme={null}
GRANT USAGE ON PROCEDURE YOUR_DATABASE.YOUR_SCHEMA.AI_OCR_FROM_STAGE_SP(STRING)
TO ROLE YOUR_CLOUDLINK_ROLE;
```
## Step 3: Create Snowflake View from Stage
Create a Snowflake view that provides access to your stage files with metadata.
Execute this SQL in your Snowflake environment:
```sql theme={null}
CREATE OR REPLACE VIEW DOCUMENT_STAGE_VIEW AS
SELECT RELATIVE_PATH,
SIZE,
LAST_MODIFIED,
MD5
FROM DIRECTORY(@DOCUMENT_STAGE);
```
* **`RELATIVE_PATH`**: File path within the stage (used to identify files for OCR processing)
* **`SIZE`**: File size in bytes
* **`LAST_MODIFIED`**: Timestamp of last file modification
* **`MD5`**: File hash for integrity checking
## Step 4: Import Stored Procedure into CloudLink
Before building your automation, import the stored procedure into Elementum through CloudLink to make it available for use.
1. Navigate to your **CloudLink** connection settings
2. Click on **Functions**
3. Select the **database** and **schema** where your stored procedure is located
4. Find your `AI_OCR_FROM_STAGE_SP` stored procedure in the list
5. Optionally **rename** it for easier identification in automations
6. Click **Save** to make it available for use in automations
Once saved, the stored procedure will appear in the **Run Function** action dropdown when building automations.
## Step 5: Import View as Elementum Table
Once your Snowflake view is created, import it into Elementum as a table.
1. Navigate to **Tables** → **Explore Data** → **CloudLink**
2. Select your Snowflake connection and choose the view you created
3. Click **Create Table** and fill out the details
## Step 6: Build Data Mine for Document Monitoring
Create a Data Mine to automatically detect when new documents arrive or existing documents change.
1. In your table, go to **Data Mining** → **Create Data Mine** → **Logic-Based Rules Mining**
2. **Identifying Columns**: Select `RELATIVE_PATH`, `LAST_MODIFIED`, and `MD5`
These columns work together to track individual files across Data Mine runs, detect when files are modified or replaced, and ensure accurate state management (ON/OFF transitions).
3. **Matching Criteria**: Set filters for file types or conditions (optional - e.g., only `.pdf` files)
4. **Name and Schedule**: Give it a name and set check frequency
## Step 7: Create Automation with Data Mine Trigger
Build an automation that processes documents when the Data Mine detects them.
Your automation will follow this logical flow: **Data Mine Trigger** → **Run OCR Function** → **Process Extracted Text** (e.g., store content in a record, trigger AI analysis)
1. Navigate to **Automations** → **Create Automation**
2. Add **Data Mine Trigger** and select your Data Mine
3. Set trigger option to **Trigger when data meets requirement**
## Step 8: Process Documents Using Run Function Action
Add a Run Function action to your automation to OCR documents using the stored procedure.
**Run Function** action details:
* **Function**: Select your `AI_OCR_FROM_STAGE_SP` stored procedure from CloudLink
* **Parameters**:
* `FILE_PATH`: `$RELATIVE_PATH` (from the Data Mine trigger)
**Variable Reference**: The `$RELATIVE_PATH` variable comes from the Data Mine trigger, providing access to all fields from the matching stage file record.
The Run Function action will return a JSON response containing:
* **`content`**: The extracted text content from the document
* **`metadata.pageCount`**: Number of pages processed
## Step 9: Work with the OCR Results
After the Run Function action completes, subsequent actions in your automation will have access to the OCR results.
Add an **Update Record** or **Create Record** action to store the extracted text in an Elementum record for future reference and searchability.
Add an **AI Action** to analyze, summarize, or categorize the extracted text content using your configured AI provider.
Add **Conditional Logic** to route documents based on extracted content (e.g., if certain keywords are detected, assign to specific team members).
## Summary
This workflow provides a powerful way to automatically extract text from documents stored in Snowflake stages:
1. **Snowflake Stage** stores your document files with encryption and directory tracking
2. **AI OCR Stored Procedure** leverages Snowflake's AI\_PARSE\_DOCUMENT for text extraction
3. **Snowflake View** makes stage files accessible with metadata
4. **CloudLink Functions** imports the stored procedure for use in automations
5. **Elementum Table** brings stage file information into your workspace
6. **Data Mine** automatically detects new or changed documents
7. **Automation** orchestrates the OCR processing workflow
8. **Run Function Action** executes the OCR procedure on each document
9. **Additional Actions** enable text analysis, storage, and intelligent workflow automation
By following this guide, you can create a robust, automated document processing system that transforms your Snowflake stage into an intelligent OCR pipeline, enabling your business to automatically extract and process text from documents as they arrive.
***
## Appendix: Complete Quick Setup
Use the following SQL to create a complete OCR processing setup in Snowflake. Replace the `ALL_CAPS` placeholders with your actual values.
```sql theme={null}
USE DATABASE DATABASE_NAME;
USE SCHEMA SCHEMA_NAME;
-- Create internal stage with directory table and encryption enabled
CREATE OR REPLACE STAGE DOCUMENT_STAGE
DIRECTORY = (ENABLE = TRUE)
ENCRYPTION = (TYPE = 'SNOWFLAKE_SSE');
-- Create AI OCR stored procedure
CREATE OR REPLACE PROCEDURE AI_OCR_FROM_STAGE_SP(FILE_PATH STRING)
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
var sql = `
SELECT TO_VARCHAR(
AI_PARSE_DOCUMENT(
TO_FILE('@DATABASE_NAME.SCHEMA_NAME.DOCUMENT_STAGE', ?),
OBJECT_CONSTRUCT('mode', 'OCR')
)
) AS response
`;
var stmt = snowflake.createStatement({
sqlText: sql,
binds: [FILE_PATH]
});
var rs = stmt.execute();
if (rs.next()) {
return rs.getColumnValue(1);
} else {
return null;
}
$$;
-- Create view for stage files
CREATE OR REPLACE VIEW DOCUMENT_STAGE_VIEW AS
SELECT RELATIVE_PATH,
SIZE,
LAST_MODIFIED,
MD5
FROM DIRECTORY(@DOCUMENT_STAGE);
```
Ensure your Elementum CloudLink role has the necessary permissions to access the stage, view, and stored procedure.
```sql theme={null}
GRANT USAGE ON DATABASE DATABASE_NAME TO ROLE CLOUDLINK_ROLE;
GRANT USAGE ON SCHEMA DATABASE_NAME.SCHEMA_NAME TO ROLE CLOUDLINK_ROLE;
GRANT USAGE ON STAGE DATABASE_NAME.SCHEMA_NAME.DOCUMENT_STAGE TO ROLE CLOUDLINK_ROLE;
GRANT SELECT ON VIEW DATABASE_NAME.SCHEMA_NAME.DOCUMENT_STAGE_VIEW TO ROLE CLOUDLINK_ROLE;
GRANT USAGE ON PROCEDURE DATABASE_NAME.SCHEMA_NAME.AI_OCR_FROM_STAGE_SP(STRING) TO ROLE CLOUDLINK_ROLE;
```
Upload a test document to verify the stage and OCR processing are working correctly:
```sql theme={null}
-- Using SnowSQL CLI
PUT file://path/to/test-document.pdf @DATABASE_NAME.SCHEMA_NAME.DOCUMENT_STAGE
OVERWRITE=TRUE
AUTO_COMPRESS=FALSE;
```
You can also upload files through the Snowflake web interface by navigating to your stage and using the "Upload Files" option.
Test your stored procedure directly in Snowflake:
```sql theme={null}
CALL AI_OCR_FROM_STAGE_SP('test-document.pdf');
```
You should receive a JSON response with the extracted text content and metadata.
## Additional Resources
* [Snowflake AI\_PARSE\_DOCUMENT Documentation](https://docs.snowflake.com/en/sql-reference/functions/ai_parse_document)
* [Accessing Files from Snowflake Stages](/administration/snowflake-stages) - For workflows that need to download files
* [Automation System](/workflows/automation-system) - Learn more about building automations
* [Data Mining](/data/data-mining) - Deep dive into Data Mine capabilities
# Snowflake Cortex Agents Setup
Source: https://docs.elementum.io/ai-agents/snowflake-cortex-agents-setup
Complete guide to integrating Snowflake Cortex Agents into Elementum Apps through Intelligence configuration
## Overview
By connecting Cortex Agents to your Elementum Apps, you enable AI-powered automation that runs directly on your Snowflake data warehouse, maintaining data security while leveraging advanced AI capabilities.
Cortex Agents are configured at the **App level** through the Intelligence settings, allowing each App to discover and use external agents available through its CloudLink connection.
## Prerequisites
Before beginning this setup, ensure you have the following in place:
### Elementum Requirements
* **App access**: Access to the App where you want to integrate Cortex Agents.
* **Snowflake AI Provider**: A configured Snowflake AI Provider with a CloudLink that uses **key-pair authentication**.
**Authentication Requirement**: Snowflake Cortex Agents require a Snowflake AI Provider configured with key-pair authentication. Password-based CloudLinks cannot access Cortex features.
### Snowflake Requirements
Your Snowflake environment must have:
* **Snowflake Edition**: Enterprise or higher
* **Cortex AI Features**: Enabled on your account
* **Cortex Agents**: At least one Cortex Agent configured in your Snowflake account
* **Permissions**: Service account with USAGE privileges on:
* Cortex functions
* Agent resources
* Target database and schema
### Verify Your Snowflake AI Provider
Before proceeding, verify you have a Snowflake AI Provider configured:
Go to **Organization Settings** → **Providers**
Verify a Snowflake provider is configured
Ensure it uses a CloudLink with **Key-Pair Authentication**
Verify the provider is active and can connect to Snowflake
**Need Help with Providers?** See the [Snowflake Cortex Setup Guide](/ai-agents/snowflake-cortex-setup) for detailed instructions on configuring Snowflake AI Providers with key-pair authentication.
## Step 1: Prepare Snowflake Cortex Agents
Before connecting to Elementum, ensure your Cortex Agents are properly configured in Snowflake.
### Verify Cortex Agents in Snowflake
1. Log into your Snowflake account
2. Navigate to **AI & ML** → **Cortex Agents**
3. Verify your agents are listed and active
4. Note the database and schema where agents are located
```sql theme={null}
-- List available Cortex Agents
SHOW CORTEX AGENTS IN DATABASE your_database;
-- View agent details
DESCRIBE CORTEX AGENT your_database.your_schema.agent_name;
```
### Required Permissions
Ensure your service account has the necessary permissions:
```sql theme={null}
-- Grant usage on database and schema
GRANT USAGE ON DATABASE your_database TO ROLE elementum_role;
GRANT USAGE ON SCHEMA your_database.your_schema TO ROLE elementum_role;
-- Grant usage on Cortex features
GRANT USAGE ON CORTEX TO ROLE elementum_role;
-- Grant execute on specific agents
GRANT USAGE ON CORTEX AGENT your_database.your_schema.agent_name
TO ROLE elementum_role;
```
**Principle of Least Privilege**: Grant only the minimum permissions necessary for the agents and data your Elementum automations will access.
## Step 2: Configure Intelligence in Your App
Now you're ready to discover and configure Snowflake Cortex Agents through your App's Intelligence settings.
### Access Intelligence Settings
Go to the App where you want to integrate Cortex Agents
Click **Intelligence** in the App menu
### Add External Agent
Click the **Add Agent** button at the top of the Intelligence page
In the agent type selection, choose **External**
This indicates you're connecting to an agent hosted outside Elementum
Choose your configured Snowflake AI Provider from the dropdown
**Provider Selection:**
* Only Snowflake providers with key-pair authentication appear
* The provider must have access to Cortex features
* Multiple providers can be available if you have different Snowflake environments
The system uses the selected provider to discover available Cortex Agents
**What happens:**
* Elementum connects to Snowflake using the provider's CloudLink credentials
* Queries the Snowflake Cortex REST API for available agents
* Lists all agents accessible through the provider's service account
From the discovered agents list, select the Cortex Agent you want to use
You'll see:
* Agent name
* Agent description
* Database and schema location
* Available capabilities
**Agent Name**: Optionally customize the display name in Elementum
**Description**: Add notes about how this agent will be used in your App
**Configuration**: Review agent input/output schemas
Click **Save** to connect the external agent
The agent will now appear in your App's Intelligence configuration
### What Happens During Discovery
When you add an external agent, Elementum:
1. **Selects Provider**: Uses the selected Snowflake AI Provider's connection
2. **Authenticates**: Authenticates with Snowflake using the provider's CloudLink credentials
3. **Discovers Agents**: Queries the Cortex REST API for available agents
4. **Retrieves Metadata**: Gets agent capabilities, schemas, and configuration
5. **Registers Agent**: Makes the agent available for use in App automations
6. **Monitors Status**: Tracks agent availability through the provider connection
```mermaid theme={null}
sequenceDiagram
participant App as App Intelligence
participant Prov as Snowflake AI Provider
participant CL as CloudLink
participant SF as Snowflake
participant API as Cortex REST API
App->>Prov: Request Agent Discovery
Prov->>CL: Use Provider CloudLink
CL->>SF: Authenticate (Key-Pair)
SF->>CL: Connection Established
Prov->>API: List Available Agents
API->>Prov: Return Agent Metadata
Prov->>App: Display Available Agents
App->>App: Register Selected Agent
```
## Step 3: Understanding Cortex REST APIs
Elementum leverages three primary Snowflake Cortex APIs for agent integration:
### Agent Discovery API
Discovers available agents accessible through your CloudLink.
**Snowflake Documentation**: [Cortex Agents REST API](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-rest-api)
**What Elementum Retrieves:**
* Agent names and identifiers
* Agent capabilities and descriptions
* Input/output schemas
* Required permissions
* Configuration metadata
### Agent Run API
Executes agent tasks and retrieves results.
**Snowflake Documentation**: [Cortex Agents Run](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-run)
**Used For:**
* Invoking agents from automations
* Passing input parameters
* Receiving agent responses
* Monitoring execution status
* Handling errors and timeouts
### Threads API
Manages conversational threads for stateful agent interactions.
**Snowflake Documentation**: [Cortex Agents Threads REST API](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-threads-rest-api)
**Capabilities:**
* Create conversation threads
* Maintain context across interactions
* Retrieve conversation history
* Resume interrupted conversations
* Manage thread lifecycle
**API Authentication**: All API calls use the credentials from the selected Snowflake AI Provider's CloudLink connection, ensuring secure and auditable access to Cortex resources.
## Step 4: Integrate Agents into Automations
With your Cortex Agent configured in Intelligence, you can now use it in App automations.
### Using External Agents in Automation Actions
In your App, go to the automation where you want to use the external agent
Add a new automation action or edit an existing one
Select **Agent Action** as the action type
In the agent configuration:
**Agent**: Choose the external Cortex Agent you configured in Intelligence
The agent will be labeled as **External** or **Managed**
**Input Mapping**: Map automation data to agent input parameters
* Use field values from the current record
* Reference previous automation action outputs
* Include static values or formulas
**Output Handling**: Configure how to handle agent responses
* Map agent outputs to record fields
* Store results for later automation actions
* Set error handling behavior
**Timeout**: Set maximum execution time (default: 60 seconds)
**Retry Policy**: Configure retry behavior for failures
**Error Handling**: Define what happens if the agent fails
* Continue automation with default values
* Halt automation and alert user
* Escalate to human review
Use the automation test mode to verify agent integration
Monitor execution logs for agent calls and responses
### Example: Data Analysis Automation
**Scenario**: Automatically analyze sales data when a monthly report is requested
```yaml theme={null}
Workflow: Monthly Sales Analysis
Trigger: Report requested
Automations:
1. Gather Data:
- Collect sales records for the month
- Aggregate by region and product
2. External Agent - Analysis:
Type: External Agent (Cortex)
Agent: Sales Analysis Agent
Inputs:
- sales_data: {aggregated_data}
- analysis_type: "trend_analysis"
- time_period: "monthly"
Outputs:
- trends: record.analysis_results
- insights: record.key_insights
- forecast: record.forecast_data
3. Generate Report:
- Create formatted report with insights
- Include visualizations from forecast
4. Distribute:
- Email report to stakeholders
- Post summary to Teams channel
```
### Using Agents in Multiple Automations
External agents configured in Intelligence can be used across multiple automations within the same App:
**Trigger**: New transaction record created
**Conditions**: Transaction amount > \$10,000
**Actions**:
1. **Call External Agent**
* Agent: Fraud Detection Agent (Cortex)
* Input: Transaction details and customer history
* Output: Risk score and explanation
2. **Conditional Logic**
* If risk\_score > 75: Flag for review and notify security team
* If risk\_score 50-75: Request additional verification
* If risk\_score \< 50: Auto-approve transaction
3. **Log Results**
* Record analysis in audit log
* Update transaction status based on outcome
## Step 5: Monitor and Maintain
### Monitoring Agent Performance
View agent execution logs in automation history
Monitor:
* Invocation frequency
* Response times
* Success/failure rates
* Error messages
Track agent usage in Intelligence settings
View:
* Active agent connections
* Last successful execution
* Configuration changes
* CloudLink status
### Provider Connection Health
Since external agents depend on the Snowflake AI Provider:
* **Monitor Provider Status**: Ensure the provider and its CloudLink remain active
* **Check Connectivity**: Regularly test Snowflake connectivity through the provider
* **Review Permissions**: Verify service account permissions haven't changed
* **Update Credentials**: Rotate provider CloudLink keys according to your security policy
**Snowflake Monitoring**: Use Snowflake's query history and warehouse monitoring to track Cortex Agent compute costs and performance. This complements Elementum's automation logs.
### Maintenance Tasks
**Weekly**:
* Review automation logs for agent errors
* Monitor agent performance metrics
* Check for timeout or failure patterns
**Monthly**:
* Review agent usage and optimization opportunities
* Audit permissions and access patterns
* Test agent functionality after Snowflake updates
**Quarterly**:
* Rotate provider CloudLink credentials
* Review and optimize automation integrations
* Evaluate new Cortex Agent capabilities
When you update agents in Snowflake:
1. **Test Changes**: Verify agent behavior in Snowflake first
2. **Refresh Intelligence**: Re-discover agents in Intelligence settings if schemas changed
3. **Update Automations**: Modify automations if input/output schemas changed
4. **Test Integrations**: Validate all automations using the updated agent
5. **Monitor**: Watch for issues after deployment
As usage grows:
**Snowflake Side**:
* Scale warehouse size for agent workloads
* Consider dedicated warehouses for different agent types
* Implement resource monitors to control costs
**Elementum Side**:
* Monitor automation execution times
* Implement caching for frequently accessed agent results
* Use async invocations for long-running agents
* Configure multiple Snowflake AI Providers for different environments or redundancy
## Configuration Best Practices
### Security Best Practices
**Minimal Permissions**: Grant only necessary privileges
**Role Separation**: Use dedicated roles for agent access
**Audit Logging**: Enable comprehensive audit trails
**Regular Reviews**: Quarterly access audits
**Rotation Schedule**: Rotate keys every 90 days
**Secure Storage**: CloudLink manages credential encryption
**No Sharing**: Unique credentials per environment
**Revocation Process**: Document emergency revocation steps
### Performance Optimization
1. **Warehouse Sizing**
* Use appropriately sized warehouses for agent complexity
* Consider multi-cluster warehouses for concurrent agent calls
* Enable auto-suspend to minimize costs
2. **Provider Management**
* Provider connections are reused across multiple agent invocations
* Monitor provider connection performance metrics
* Configure appropriate timeout values
3. **Timeout Configuration**
* Set realistic timeouts based on agent complexity
* Implement progressive timeout strategies
* Consider async patterns for very long-running operations
4. **Caching Strategies**
* Cache agent responses for identical inputs
* Implement time-based cache invalidation
* Use Snowflake result caching when appropriate
### Error Handling Patterns
**Transient Failures**: Implement exponential backoff for temporary issues
```
Attempt 1: Immediate
Attempt 2: Wait 2 seconds
Attempt 3: Wait 4 seconds
Attempt 4: Wait 8 seconds
Max Attempts: 4
```
**Permanent Failures**: Don't retry for authentication or permission errors
**Circuit Breaker**: Stop attempting after consecutive failures reach threshold
**Default Values**: Use sensible defaults when agent unavailable
**Alternative Agents**: Configure backup agents for critical operations
**Human Escalation**: Route to human review when automated analysis fails
**Graceful Degradation**: Continue automation with reduced functionality
## Troubleshooting
### Discovery Issues
**Error**: "No agents found" when adding external agent
**Possible Causes**:
* Snowflake AI Provider not configured or inactive
* Provider's CloudLink has wrong permissions
* No Cortex Agents configured in Snowflake
* Service account lacks USAGE privileges
**Solutions**:
1. Verify Snowflake AI Provider is configured in Organization Settings → Providers
2. Check that provider uses a CloudLink with key-pair authentication
3. Run `SHOW CORTEX AGENTS` in Snowflake to verify agents exist
4. Verify service account has USAGE privileges on Cortex
5. Check database and schema permissions
6. Test provider connection in Provider settings
**Error**: "Unable to authenticate with Snowflake"
**Possible Causes**:
* Provider CloudLink not configured with key-pair authentication
* Expired or invalid credentials
* Network connectivity issues
* Snowflake account unavailable
**Solutions**:
1. Verify provider uses CloudLink with key-pair authentication (not password)
2. Test provider connection in Organization Settings → Providers
3. Check for expired credentials and refresh provider if needed
4. Verify network connectivity to Snowflake
5. Confirm Snowflake account is active and accessible
6. Review provider configuration and CloudLink settings
**Error**: "Access denied to Cortex resources"
**Possible Causes**:
* Missing USAGE grant on Cortex
* Insufficient privileges on agent
* Role not properly assigned
* Database or schema access missing
**Solutions**:
```sql theme={null}
-- Verify current role and grants
SHOW GRANTS TO ROLE elementum_role;
-- Grant necessary permissions
GRANT USAGE ON DATABASE your_database TO ROLE elementum_role;
GRANT USAGE ON SCHEMA your_database.your_schema TO ROLE elementum_role;
GRANT USAGE ON CORTEX TO ROLE elementum_role;
GRANT USAGE ON CORTEX AGENT your_database.your_schema.agent_name
TO ROLE elementum_role;
```
### Runtime Issues
**Error**: "Agent execution exceeded timeout"
**Possible Causes**:
* Agent task too complex for timeout setting
* Insufficient Snowflake warehouse resources
* Network latency issues
* Agent accessing large datasets
**Solutions**:
1. Increase timeout value in automation configuration
2. Scale up Snowflake warehouse for agent workload
3. Optimize agent queries and data access
4. Consider splitting complex tasks into multiple automation actions
5. Use async invocation for long-running operations
**Error**: Agent output doesn't match expected format
**Possible Causes**:
* Input parameters incorrect or malformed
* Agent configuration changed in Snowflake
* Schema version mismatch
* Data quality issues in source data
**Solutions**:
1. Validate input parameters match agent expectations
2. Test agent directly in Snowflake with same inputs
3. Check for recent agent updates or schema changes
4. Review agent logs in Snowflake for execution details
5. Verify data quality and completeness
**Error**: Agent responses taking too long
**Possible Causes**:
* Undersized Snowflake warehouse
* Network latency between Elementum and Snowflake
* Agent querying large datasets inefficiently
* Cold warehouse startup time
**Solutions**:
1. Use larger warehouse for agent operations
2. Keep warehouse running during peak usage (disable auto-suspend temporarily)
3. Optimize agent queries and data access patterns
4. Consider warehouse dedicated to agent workloads
5. Implement result caching for repeated queries
## Multi-App Configurations
### Using Agents Across Multiple Apps
Each App configures its own external agents through Intelligence:
* **Configure per App**: Each App that needs Cortex Agents must configure them individually, even if multiple Apps connect to the same Snowflake agents.
* **Provider selection**: Apps can use the same Snowflake AI Provider (shared access) or different providers (isolated access or different Snowflake environments).
* **Independent configuration**: Each App can select different providers, use different agents from the same provider, configure the same agent differently, and define unique timeout and error handling settings.
* **Monitor separately**: Track agent usage and performance per App in each App's Intelligence dashboard.
### Multi-Environment Setup
For organizations with multiple environments (dev, staging, production):
Configure separate Snowflake AI Providers for each environment
Each provider uses a CloudLink with environment-specific service accounts
In each environment's App, configure external agents through Intelligence
Select the appropriate provider (dev/staging/prod) to discover agents
Test in dev environment before deploying to production
Validate in staging with production-like data
When deploying configurations:
* Test agent connectivity in target environment
* Verify provider configuration is correct for the environment
* Validate automation configurations
* Monitor closely after deployment
## Example Use Cases
**Scenario**: Automatically validate data quality when new datasets are loaded
**Implementation**:
1. Create Cortex Agent in Snowflake for data profiling and quality analysis
2. In your Data Management App, configure the agent through Intelligence
3. Set up automation trigger on data load completion
4. Agent analyzes dataset and returns quality metrics
5. Automation creates alerts or blocks further processing based on results
**Benefits**:
* Immediate data quality feedback
* Prevents downstream issues from bad data
* Reduces manual validation effort
* Maintains audit trail of quality checks
**Scenario**: L1 support agent with direct access to customer data warehouse
**Implementation**:
1. Create Cortex Agent trained on support knowledge base and customer data
2. Configure agent in Support App through Intelligence
3. Integrate agent into support automation
4. Customer inquiry triggers agent to analyze history and suggest resolution
5. Agent creates ticket with context and recommended actions
**Benefits**:
* Faster response times for customers
* Consistent support quality
* Reduces escalations to L2 support
* Data stays secure in Snowflake environment
**Scenario**: Generate executive reports with natural language insights
**Implementation**:
1. Create Cortex Agent for financial analysis and narrative generation
2. Configure agent in Finance App through Intelligence
3. Schedule monthly report generation automation
4. Agent analyzes trends, generates insights, and creates narrative
5. Report distributed automatically with executive summary
**Benefits**:
* Consistent reporting schedule
* Natural language insights for non-technical stakeholders
* Reduces analyst workload
* Real-time access to latest data
## Next Steps
Understand the technical architecture and A2A protocol
Learn how to build sophisticated automations with agents
Reference Snowflake's official Cortex Agents documentation
***
*By integrating Snowflake Cortex Agents through your App's Intelligence configuration, you create a powerful automation ecosystem that combines the flexibility of AI with the security of keeping your data in your own warehouse. This architecture ensures compliance, performance, and scalability for enterprise deployments.*
# Snowflake Cortex Setup
Source: https://docs.elementum.io/ai-agents/snowflake-cortex-setup
Configure Snowflake Cortex as your AI provider for language model and embedding services
## Overview
Snowflake Cortex brings AI capabilities directly to your data warehouse, allowing you to run LLMs and embedding models on your data without moving it outside your secure environment. This guide walks you through setting up Snowflake Cortex as an AI Provider in Elementum.
Snowflake Cortex AI features are only available when your Snowflake CloudLink uses **key-pair authentication**. Password authentication cannot access these capabilities. If you haven't connected Snowflake yet, complete [Connect Snowflake to Elementum](/administration/connect-snowflake-to-elementum) first—this guide assumes that's done. For background on CloudLink itself, see the [CloudLink Overview](/administration/cloudlink-overview).
## Step 1: Verify CloudLink prerequisites
Before setting up Snowflake Cortex, confirm your Snowflake CloudLink is in place:
1. Go to **Organization Settings** → **CloudLinks** and verify your Snowflake connection is active.
2. Confirm the CloudLink uses **key-pair authentication**. If it doesn't, follow [Key rotation](/administration/connect-snowflake-to-elementum#key-rotation) on the Snowflake setup page to migrate.
3. Confirm the [Cortex AI grants](/administration/connect-snowflake-to-elementum#step-4-grant-permissions-and-set-the-network-policy) have been applied to the `ELEMENTUM` role.
### Snowflake Account Requirements
Your Snowflake account must meet the following requirements for Cortex AI access:
* **Snowflake Edition**: Enterprise or higher
* **Cortex Features**: Enabled and available in your region (most AWS, Azure, and GCP regions are supported)
* **Permissions**: USAGE privileges on Cortex functions for your service account
* **Billing**: Cortex usage is billed through your Snowflake account
## Step 2: Configure Snowflake Cortex in Elementum
When you have a CloudLink connection with key-pair authentication, Elementum automatically discovers available Snowflake Cortex capabilities.
### Add the Provider
1. In Elementum, go to **Organization Settings** and select the **Providers** tab
2. Click **+ Provider** and select **Snowflake** — you'll see your existing CloudLink connections listed
3. Configure the provider settings:
**Provider Name**: Enter a descriptive name (e.g., "Snowflake Cortex AI")
**CloudLink**: Select your key-pair authenticated CloudLink
**Service Account Credentials**: Auto-populated from your CloudLink
If automatic discovery doesn't populate your settings, you may need to configure manually:
**Provider Name**: Descriptive name for your Snowflake provider
**Location**: Your Snowflake region and account details
**Project ID**: Your Snowflake account identifier
**CloudLink**: Select the appropriate CloudLink connection
4. Click **Save** to create the provider. Elementum will automatically validate your connection and discover available models — look for a green checkmark indicating a successful connection.
## Step 3: Create your first AI service
With your Snowflake Cortex provider configured, create an AI Service that uses a Cortex model. See [AI Services](/ai-agents/ai-services) for the full walkthrough, including LLM and embedding service configuration, assignment, and failover.
For a side-by-side comparison of Cortex LLMs and embedding models—including recommendations for daily tasks vs. complex reasoning and embedding quality tiers—see [AI Models](/ai-agents/ai-models).
**Embeddings for AI Search**: Snowflake Cortex is the only provider that supports embedding services in Elementum. If you plan to use [AI Search](/ai-agents/ai-search), create an embedding service from this provider.
**Model availability**: Available models depend on your Snowflake account tier, region, and current Cortex offerings. Model selection may vary over time.
## Usage Guidelines
### Cost Management
Snowflake Cortex usage is billed through your Snowflake account. To manage costs:
* Monitor Cortex function usage in the Snowflake console
* Track warehouse usage for AI workloads
* Set up Snowflake resource monitors and billing alerts
* Review token consumption regularly
* Use appropriate models for each task (Claude 3.7 Sonnet for routine work, Opus 4 only when necessary)
* Process multiple requests in batches when possible
* Cache frequent AI results to avoid redundant calls
* Scale warehouses appropriately — larger models may need bigger warehouses
### Best Practices
* Use **Claude 3.7 Sonnet** for most daily automation and customer support tasks
* Use **Claude Sonnet 4** for advanced reasoning and premium applications
* Reserve **Claude Opus 4** for the most complex tasks requiring maximum intelligence
* Use **Mistral Large 2** for European regulatory compliance and multilingual tasks
* Be specific and clear in your prompts
* Use system messages for consistent behavior
* Provide examples for better results
* Structure complex problems step-by-step for reasoning models
* Scale warehouses based on model complexity and concurrent usage
* Enable auto-scaling for variable workloads
* Choose models appropriate for the task complexity — avoid over-provisioning
* Implement result caching for repeated queries
## Troubleshooting
**Symptoms:** Cannot access Snowflake Cortex AI functions
**Common Causes:**
* Using password authentication instead of key-pair
* Insufficient permissions on Cortex functions
* Account doesn't have Cortex access
**Solutions:**
1. Verify key-pair authentication is configured on your CloudLink
2. Check USAGE privileges on Cortex functions
3. Contact Snowflake support for account access
4. Verify account edition (Enterprise or higher) and region support
**Symptoms:** Expected models don't appear in service creation
**Common Causes:**
* Regional model availability
* Account tier limitations
* CloudLink connection issues
**Solutions:**
1. Verify CloudLink connection is active
2. Check regional model availability in Snowflake documentation
3. Review account tier and permissions
4. Refresh provider configuration
**Symptoms:** Slow AI response times or timeouts
**Common Causes:**
* Undersized warehouse for AI workloads
* Inefficient query patterns
* Large data volumes
**Solutions:**
1. Scale up warehouse size
2. Optimize data queries
3. Implement result caching
4. Consider dedicated warehouses for AI workloads
## Security Considerations
Snowflake Cortex runs AI directly on your data warehouse, which provides key security advantages:
* Data never leaves your Snowflake environment
* Maintains existing data governance and compliance policies
* Leverages Snowflake's built-in security model and encryption
* All access is auditable through Snowflake's audit logging
* CloudLink manages credential encryption and secure storage
* Rotate key-pair credentials according to your security policy
* Use dedicated service accounts with minimal permissions
* Monitor service account usage in Snowflake for anomalies
## Next Steps
With Snowflake Cortex configured as your AI Provider:
Set up specific LLM and embedding services using Cortex models
Use Snowflake embeddings for intelligent search on your data
Create agents that can directly access your Snowflake data
Integrate Snowflake Cortex Agents into your Apps
# Studio Agents
Source: https://docs.elementum.io/ai-agents/studio-agents
Build automations, agents, and flows in Elementum through natural language conversation with a coding-based agent
## Overview
Studio Agents are a coding-based agent type in Elementum that build automations, agents, and flows through conversation. Describe what you need in natural language, and the Studio Agent writes the TypeScript that assembles the flow inside your app—lowering the barrier to creating complex workflows without hand-building every stage, decision point, and automation.
Studio Agents live on the **Flows** page of your app and stay available whenever you're building or iterating on a workflow. The agent handles the underlying code while you review the generated flow in a live **Preview** pane, refine it in the chat, and publish when it looks right.
Studio Agents run on Anthropic Claude models. Configure an [Anthropic AI provider](/ai-agents/anthropic-setup) and select a supported Claude model (for example, **Claude 4.5 Sonnet**) before starting a Studio Agent session. See [AI Models](/ai-agents/ai-models#anthropic-direct) for model capabilities.
## Prerequisites
Before you can use a Studio Agent, make sure your organization has:
* An [Anthropic AI provider](/ai-agents/anthropic-setup) configured in **Organization Settings**.
* At least one Claude LLM service available to the app where you plan to build the flow. See [AI Services](/ai-agents/ai-services) for setup steps.
* App Administrator access on the app whose flow you want to build.
## Start a Studio Agent session
1. Open your app and click **Flows** in the left navigation menu.
2. In the natural language chat at the top of the **Flows** page, describe the workflow you want to build. Explain the stages, decision points, and automations you'd like included.
3. Click the **Send** icon to hand the request off to the Studio Agent.
4. Follow along in the chat pop-up as the agent writes the TypeScript that builds the flow. Click any action in the chat to see more details about what the agent did.
5. Use the **Preview** pane to verify the workflow is being built as expected.
6. If something doesn't look right, keep chatting with the agent to refine the flow. The agent maintains context across the session, so you can iterate stage by stage or make broad changes in a single message.
7. Click **Publish** when the flow is ready.
After publishing, the flow behaves like any other flow in your app—you can open it, add or remove stages, edit each step manually, and connect it to the rest of your app's automations, elements, and views.
## Prompt templates
Below the chat on the **Flows** page, Elementum surfaces pre-built prompt templates you can use as a starting point instead of writing a prompt from scratch. Examples include:
* **Expense approval** — Multi-stage approval workflow with reviewer routing and status updates.
* **Customer onboarding** — Sequenced steps for kicking off a new customer, gathering intake data, and triggering follow-up tasks.
* **Incident triage** — Intake, classification, and escalation of incoming issues.
Select a template to prefill the chat with a starter prompt, then edit it to match the specifics of your app before sending it to the Studio Agent.
## Resume a session
In-progress builds are saved automatically as you chat with the agent. To pick a session back up later:
1. Open the **Flows** page in your app.
2. Scroll to **Studio Agent Sessions** to see drafts that haven't been published yet.
3. Select the session you want to continue. The chat, preview, and all prior context are restored so you can keep iterating with the agent.
Because the Studio Agent generates the underlying TypeScript, you don't need to worry about losing your work between sessions—the full conversation, generated code, and current preview state are all preserved.
## After you publish
Published flows are fully editable by hand. Use the flow editor to:
* Rename or reorder stages the agent created.
* Add or remove automations, approval steps, or assignment rules.
* Wire the flow into other parts of your app—for example, [managed views](/workflows/managed-views), [Elements](/workflows/object-data-access), or [record layouts](/workflows/layouts).
If you want to keep iterating with a Studio Agent after publishing, start a new session on the same **Flows** page and describe the changes you'd like the agent to make.
## Related documentation
Configure the Anthropic AI provider that powers Studio Agents
Compare Claude models and choose the right one for Studio Agents
Create and configure conversational agents in your app
Learn how the automations Studio Agents build fit into your app
# Upcoming Model Deprecations
Source: https://docs.elementum.io/ai-agents/upcoming-model-deprecations
Schedule of AI model deprecations, recommended replacements, and how to get notified when a model leaves the supported list
This page lists AI models scheduled for deprecation and recommended replacements. For capabilities and when to use each model, see [AI Models](/ai-agents/ai-models).
The RSS feed for this page publishes when a model is added to the schedule and includes the date it will be deprecated.
## Upcoming
### Open AI
**Engine version:** `gpt-3.5-turbo-1106`
**Recommended replacement:** `GPT_5_4_MINI`
**Engine version:** `gpt-3.5-turbo`
**Recommended replacement:** `GPT_4_1_MINI` or `GPT_5_4_MINI`
**Engine version:** `gpt-4`
**Recommended replacement:** `GPT_4_1`
**Engine version:** `gpt-4.1-nano`
**Recommended replacement:** `GPT_5_NANO` or `GPT_5_4_NANO`
**Engine version:** `o3-mini`
**Recommended replacement:** `GPT_5_4_MINI` (supports reasoning)
### Gemini
**Engine version:** `gemini-2.5-pro`
**Recommended replacement:** `GEMINI_3_PRO`
**Engine version:** `gemini-2.5-flash`
**Recommended replacement:** `GEMINI_2_0_FLASH` or `GEMINI_3_PRO`
## Requires Attention
| Enum | Engine Version | Platform | Notes | Recommended Replacement |
| ----------------- | -------------- | ---------- | --------------------------- | ---------------------------------- |
| `OPEN_AI_O1_MINI` | `o1-mini` | OpenAI API | Deprecated October 27, 2025 | `OPEN_AI_O3_MINI` → `GPT_5_4_MINI` |
## Previously deprecated
| Enum | Engine Version | Platform | Deprecation Date | Recommended Replacement |
| ------------------------ | --------------------- | ---------------- | ---------------- | ---------------------------------------- |
| `GPT_4_TURBO_PREVIEW` | `gpt-4-turbo-preview` | OpenAI API | March 26, 2026 | `GPT_4_1` or `GPT_5_4` |
| `CLAUDE_3_5_SONNET` | `claude-3-5-sonnet` | Snowflake Cortex | March 31, 2026 | `CLAUDE_4_6_SONNET` |
| `CORTEX_OPEN_AI_O4_MINI` | `openai-o4-mini` | Snowflake Cortex | April 16, 2026 | `CORTEX_GPT_5_MINI` |
| `CLAUDE_3_7_SONNET` | `claude-3-7-sonnet` | Snowflake Cortex | April 28, 2026 | `CLAUDE_4_6_SONNET` |
| `SNOWFLAKE_ARCTIC` | `snowflake-arctic` | Snowflake Cortex | April 28, 2026 | `LLAMA3_3_70B` or `MISTRAL_LARGE_2` |
| `CLAUDE_4_OPUS` | `claude-4-opus` | Snowflake Cortex | May 1, 2026 | `CLAUDE_4_6_SONNET` or `CLAUDE_4_6_OPUS` |
## References
* [Snowflake Cortex April 2026 Deprecations](https://docs.snowflake.com/en/release-notes/bcr-bundles/un-bundled/bcr-april-model-deprecations)
* [Snowflake Cortex May 2026 Deprecations](https://docs.snowflake.com/en/release-notes/bcr-bundles/un-bundled/bcr-may-model-deprecations)
* [OpenAI Deprecations](https://developers.openai.com/api/docs/deprecations)
* [Google Vertex AI Gemini 2.5 Retirement Notice](https://cloud.google.com/vertex-ai/generative-ai/docs/deprecations)
## Stay notified
Use the RSS button on this page to hear when a model is added to the deprecation schedule. The feed includes the date the model will be deprecated. Subscribe below to get an email when a model is removed from Elementum's supported list.
# Elementum API
Source: https://docs.elementum.io/api-reference/api-introduction
Get started with the Elementum API to integrate with your systems and streamline workflow automation.
## Overview
Our API is designed to provide you with programmatic access to core features of Elementum, enabling you to build integrations, automate workflows, and extend the capabilities of your Elementum workspace.
This guide will walk you through the essential steps to get started, from authentication to making your first API call.
View the full OpenAPI specification file for a complete list of endpoints and schemas.
Check the current status of our API services and get notified of any issues.
## Authentication
API access is at the user level. You create a Client ID and Secret for a user; those credentials are used to obtain a Bearer token. Tokens expire after 24 hours and are required for all API requests.
### Step 1: Create API credentials
1. Sign in to Elementum in your web browser
2. Open the **User Settings** menu and go to the **OAuth** section
3. Select **Create New Token**, choose **API Access**, and click **Generate Token**
4. Save the generated **Client ID** and **Client Secret** immediately — they are shown only once and cannot be retrieved later
Store your Client ID and Client Secret securely. They provide access to your Elementum data. Do not expose them in frontend applications or public repositories.
### Step 2: Obtain a Bearer token
Request an access token from the OAuth 2.0 endpoint. Use this step each time you need a new Bearer token (for example, after expiry).
* **Endpoint:** `POST https://api.elementum.io/oauth/token` (EU: `https://api.eu.elementum.io/oauth/token`)
* **Content-Type:** `application/x-www-form-urlencoded`
* **Authorization:** Basic Auth with your Client ID as the username and Client Secret as the password (base64-encoded `client_id:client_secret`)
* **Body:** `grant_type=client_credentials`
Example with cURL:
```bash theme={null}
curl -X POST 'https://api.elementum.io/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--user 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \
--data 'grant_type=client_credentials'
```
A successful response returns an `access_token`. Use it in the `Authorization` header as `Bearer {access_token}` for all API requests. Records created or updated via the API are attributed to the API user.
### Step 3: Make authenticated API calls
Include the access token in the `Authorization` header:
```bash theme={null}
curl -X GET 'https://api.elementum.io/v1/apps/your-app-namespace' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'Content-Type: application/json'
```
Replace `YOUR_ACCESS_TOKEN`, the record type, and namespace with your values. See [Record Types and Namespaces](#record-types-and-namespaces) and the endpoint reference below.
## Base URL
All API requests are made to the following base URL. All endpoints in this documentation are listed relative to this base URL.
`https://api.elementum.io/v1`
For users in the EU region, use the EU endpoint: `https://api.eu.elementum.io/v1`
## Core Concepts
### Record Types and Namespaces
The API supports record types such as **Apps**, **Tasks**, and **Elements**. You must specify the record type and its **alias** (namespace) in the path. Aliases are unique identifiers for the app, element, or task.
**Where to find the alias:**
* In the **Create Record** modal for that record type
* In **Admin** — aliases can be set in the Create Definition (or equivalent) modals for the app, element, or task
Example endpoint structure: `/{recordType}/{alias}` (e.g. `/{recordType}/{alias}/{id}` for a specific record).
### File Attachments
The Elementum API supports adding attachments to records via the attachment endpoints. When working with file uploads:
* **Maximum File Size**: **250MB** per file
* **Supported Operations**: Upload attachments, add URL links, delete attachments
* **File Storage**: Files are stored as attachments on records and can be accessed via the attachment URL
* **Common Use Cases**: Document uploads, image attachments, report files, contracts
**Example Endpoint**:
```
POST https://api.elementum.io/v1/elements/testelement/TTE-11/attachments
```
See the [Attachments API endpoints](/api-reference/endpoints/attachments/add-an-attachment) for specific implementation details.
## Filtering Results
Search endpoints accept **RSQL** filter strings in the `filter` query parameter. Use the operators below in your filter expressions.
### Operators
| Operator | Description | Example |
| ----------------- | ------------------- | ------------------------------------------------------------------ |
| `==` | Equal To | `Status==Open` |
| `!=` | Not Equal To | `Status!=Closed` |
| `=gt=` | Greater Than | `Created on=gt=2022-01-01T00:00:00.000Z` |
| `=ge=` | Greater Or Equal To | `Created on=ge=2022-01-01T00:00:00.000Z` |
| `=lt=` | Less Than | `Created on=lt=2022-04-01T00:00:00.000Z` |
| `=le=` | Less Or Equal To | `Created on=le=2022-04-01T00:00:00.000Z` |
| `=bt=` | Between | `Created on=bt=2022-01-01T00:00:00.000Z:2022-04-01T00:00:00.000Z` |
| `!bt=` or `=nbt=` | Not Between | `Created on=!bt=2022-01-01T00:00:00.000Z:2022-04-01T00:00:00.000Z` |
| `=in=` | In | `Status=in=Open:Closed` |
| `=out=` | Not In | `Priority=out=Low:Medium:High` |
| `=lk=` | Like | `Title=lk=Order 1234` |
### Combining Filters
* **AND**: Use semicolon (`;`) — e.g. `Status==Open;Priority==High`. In URLs, encode as `%3b`.
* **OR**: Use comma (`,`) — e.g. `Status==Open,Status==Closed`. In URLs, encode as `%2c`.
AND operators take precedence over OR operators in filter expressions. URL-encode special characters when passing filters in query parameters.
## Error Handling
The Elementum API uses standard HTTP status codes to indicate the success or failure of an API request.
| Status Code | Meaning |
| --------------------------- | ------------------------------------------------------------------------ |
| `200 OK` | The request was successful. |
| `201 Created` | The resource was successfully created. |
| `202 Accepted` | The request was accepted for processing, but has not yet been completed. |
| `400 Bad Request` | The request was improperly formatted or contained invalid parameters. |
| `401 Unauthorized` | Your access token is wrong, expired, or you did not provide one. |
| `403 Forbidden` | You don't have permission to access the requested resource. |
| `404 Not Found` | The requested resource could not be found. |
| `429 Too Many Requests` | You're sending too many requests. |
| `500 Internal Server Error` | We had a problem with our server. Try again later. |
## Rate Limiting
To ensure the stability of our services for all users, the Elementum API enforces rate limiting. If you exceed the rate limit, you will receive an HTTP `429 Too Many Requests` response.
## Endpoint reference
The base URL is `https://api.elementum.io/v1` (EU: `https://api.eu.elementum.io/v1`). Supported endpoints by area:
| Function | Method | Path |
| ------------------------- | ------ | ------------------------------------------------------- |
| Search for record(s) | GET | `/{recordType}/{alias}` |
| Find record by ID | GET | `/{recordType}/{alias}/{id}` |
| Create a record | POST | `/{recordType}/{alias}` |
| Update a record | PUT | `/{recordType}/{alias}/{id}` |
| Get related items | GET | `/{recordType}/{alias}/{id}/related-items` |
| Add related item | POST | `/{recordType}/{alias}/{id}/related-items` |
| Remove related item | DELETE | `/{recordType}/{alias}/{id}/related-items/{relationId}` |
| Find attachment by ID | GET | `/{recordType}/{alias}/{id}/attachments/{attachmentId}` |
| Get list of attachments | GET | `/{recordType}/{alias}/{id}/attachments` |
| Add attachment | POST | `/{recordType}/{alias}/{id}/attachments` |
| Add link | POST | `/{recordType}/{alias}/{id}/attachments/url-links` |
| Remove attachment | DELETE | `/{recordType}/{alias}/{id}/attachments/{attachmentId}` |
| Get watchers | GET | `/{recordType}/{alias}/{id}/watchers` |
| Add watcher(s) | POST | `/{recordType}/{alias}/{id}/watchers` |
| Remove watcher(s) | DELETE | `/{recordType}/{alias}/{id}/watchers` |
| Get comments | GET | `/{recordType}/{alias}/{id}/comment` |
| Add comment | POST | `/{recordType}/{alias}/{id}/comment` |
| List users | GET | `/users` |
| Create a user | POST | `/users` |
| Get a user by ID | GET | `/users/{userId}` |
| Deactivate a user | POST | `/users/{userId}/deactivate` |
| List groups | GET | `/groups` |
| Get a group by ID | GET | `/groups/{groupId}` |
| List users in a group | GET | `/groups/{groupId}/users` |
| Add users to a group | POST | `/groups/{groupId}/users` |
| Remove users from a group | DELETE | `/groups/{groupId}/users` |
When `recordType` is `elements`, see the [tip on reliably listing element records](/api-reference/endpoints/records/get-the-list-of-records) before calling the search endpoint through an interactive API explorer.
See the [API Reference](/api-reference/endpoints/records/get-the-list-of-records) sections for parameters, request bodies, and response schemas.
## Best Practices and important notes
* **Field names and picklist values** match what is configured in the record type definition in Admin. Use the exact names and values from your app.
* **Date and date-time fields** must be in ISO 8601 format with UTC (`Z`) offset: `YYYY-MM-DDTHH:MM:SS.SSSZ` — for example, `2026-06-05T14:30:00.000Z`. Mixing local-timezone offsets can cause display inconsistencies; always send UTC.
* **Record creation** must include all required fields defined for that record type.
* **Relating items** (e.g. adding Elements to an App record) is done one item at a time.
* URL-encode special characters in filter strings when using query parameters.
* Access tokens expire after 24 hours; obtain a new token when needed.
## Versioning
Our API is versioned to ensure that changes are predictable and non-breaking. The current version is `v1`, which is specified in the URL of your API requests.
`https://api.elementum.io/v1/{endpoint}`
# Request an access token
Source: https://docs.elementum.io/api-reference/endpoints/access-token/request-an-access-token
post /oauth/token
Obtain a Bearer token using OAuth 2.0 client credentials. Send either (1) JSON body with client_id, client_secret, and grant_type, or (2) application/x-www-form-urlencoded body with grant_type=client_credentials and Basic Auth header (Client ID as username, Client Secret as password). Tokens expire after 24 hours.
# Add a link
Source: https://docs.elementum.io/api-reference/endpoints/attachments/add-a-link
post /{recordType}/{alias}/{id}/attachments/url-links
# Add an Attachment
Source: https://docs.elementum.io/api-reference/endpoints/attachments/add-an-attachment
post /{recordType}/{alias}/{id}/attachments
Upload a file attachment to a specific record
## Overview
Upload file attachments to records in Elementum. Files are stored as attachments on the record and accessible through the record's attachments block.
**File Size Limit**: Maximum file size is **250MB** per attachment.
## Endpoint
```
POST https://api.elementum.io/v1/elements/{elementname}/{record-handle}/attachments
```
## Request
The request body should be `multipart/form-data` containing the file to upload.
**Path Parameters:**
* `elementname` - The namespace of your element (e.g., `testelement`)
* `record-handle` - The unique identifier for the record (e.g., `TTE-11`)
**Headers:**
* `Authorization: Bearer {access_token}`
* `Content-Type: multipart/form-data`
**Body:**
* `file` (required) - The file to upload
* `description` (optional) - Description for the attachment
## Example Request
```bash theme={null}
curl -X POST 'https://api.elementum.io/v1/elements/testelement/TTE-11/attachments' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-F 'file=@/path/to/document.pdf' \
-F 'description=Contract document'
```
## Response
**Success (202 Accepted):**
```json theme={null}
{
"id": "att_abc123xyz",
"name": "document.pdf",
"description": "Contract document",
"mediaType": "application/pdf",
"size": 2458624,
"state": "processing",
"createdAt": "2025-01-08T14:30:00Z"
}
```
The `202 Accepted` status indicates the file upload has been accepted and is being processed asynchronously.
**Common Errors:**
* `400` - Invalid file or exceeds size limit
* `401` - Invalid or expired access token
* `404` - Record not found
* `413` - File exceeds 250MB size limit
## Related Documentation
Comprehensive guide to working with files and attachments
Add a URL link as an attachment
Remove an attachment from a record
Authentication and getting started
# Delete a record attachment
Source: https://docs.elementum.io/api-reference/endpoints/attachments/delete-a-record-attachment
delete /{recordType}/{alias}/{id}/attachments/{attachmentId}
# Download attachment content
Source: https://docs.elementum.io/api-reference/endpoints/attachments/download-attachment-content
get /{recordType}/{alias}/{id}/attachments/{attachmentId}/content
# Find an attachment by ID
Source: https://docs.elementum.io/api-reference/endpoints/attachments/find-an-attachment-by-id
get /{recordType}/{alias}/{id}/attachments/{attachmentId}
# Get the list of attachments
Source: https://docs.elementum.io/api-reference/endpoints/attachments/get-the-list-of-attachments
get /{recordType}/{alias}/{id}/attachments
# Add a comment
Source: https://docs.elementum.io/api-reference/endpoints/comments/add-a-comment
post /{recordType}/{alias}/{id}/comment
# Get the list of comments
Source: https://docs.elementum.io/api-reference/endpoints/comments/get-the-list-of-comments
get /{recordType}/{alias}/{id}/comment
# Add users to a group
Source: https://docs.elementum.io/api-reference/endpoints/groups/add-users-to-a-group
post /groups/{groupId}/users
# Get a group by ID
Source: https://docs.elementum.io/api-reference/endpoints/groups/get-a-group-by-id
get /groups/{groupId}
# List groups in your organization
Source: https://docs.elementum.io/api-reference/endpoints/groups/list-groups
get /groups
# List users in a group
Source: https://docs.elementum.io/api-reference/endpoints/groups/list-users-in-a-group
get /groups/{groupId}/users
# Remove users from a group
Source: https://docs.elementum.io/api-reference/endpoints/groups/remove-users-from-a-group
delete /groups/{groupId}/users
# Create a record
Source: https://docs.elementum.io/api-reference/endpoints/records/create-a-record
post /{recordType}/{alias}
# Find a record by ID
Source: https://docs.elementum.io/api-reference/endpoints/records/find-a-record-by-id
get /{recordType}/{alias}/{id}
# Get the list of records
Source: https://docs.elementum.io/api-reference/endpoints/records/get-the-list-of-records
get /{recordType}/{alias}
When listing **Element-type records**, calling this endpoint through an interactive API explorer can resolve to the Elements management API (`GET /elements/{alias}`, "Get an element by alias") instead of the record list, because both share the `/elements/{alias}` path shape.
To reliably reach the records list, call the endpoint directly — for example with cURL or Postman — using the full path `https://api.elementum.io/v1/{recordType}/{alias}`.
To retrieve one known record by ID instead of a list, `GET https://api.elementum.io/v1/elements/{namespace}/{recordId}` returns that single record directly.
# Update a record
Source: https://docs.elementum.io/api-reference/endpoints/records/update-a-record
put /{recordType}/{alias}/{id}
# Add a related item
Source: https://docs.elementum.io/api-reference/endpoints/related-items/add-a-related-item
post /{recordType}/{alias}/{id}/related-items
# Get the list of related items
Source: https://docs.elementum.io/api-reference/endpoints/related-items/get-the-list-of-related-items
get /{recordType}/{alias}/{id}/related-items
# Remove a related item
Source: https://docs.elementum.io/api-reference/endpoints/related-items/remove-a-related-item
delete /{recordType}/{alias}/{id}/related-items/{itemId}
# Create a user in your organization
Source: https://docs.elementum.io/api-reference/endpoints/users/create-a-user
post /users
# Deactivate a user
Source: https://docs.elementum.io/api-reference/endpoints/users/deactivate-a-user
post /users/{userId}/deactivate
# Get a user by ID
Source: https://docs.elementum.io/api-reference/endpoints/users/get-a-user-by-id
get /users/{userId}
# List users in your organization
Source: https://docs.elementum.io/api-reference/endpoints/users/list-users
get /users
# Add a watcher
Source: https://docs.elementum.io/api-reference/endpoints/watchers/add-a-watcher
post /{recordType}/{alias}/{id}/watchers
# Delete a watcher
Source: https://docs.elementum.io/api-reference/endpoints/watchers/delete-a-watcher
delete /{recordType}/{alias}/{id}/watchers
# Get the list of watchers
Source: https://docs.elementum.io/api-reference/endpoints/watchers/get-the-list-of-watchers
get /{recordType}/{alias}/{id}/watchers
# Analytics
Source: https://docs.elementum.io/data/analytics
Build charts and dashboards from Apps, Elements, Tasks, Tables, and CloudLinks data with aggregations, time ranges, and filters
Analytics turns
Tables,
Elements,
Tasks,
Apps, and CloudLink-backed data into charts and dashboards. You can aggregate fields, group by time, filter to subsets, and place results on dashboards with widgets that refresh as source data changes.
**Analytics vs Reports:** Analytics are interactive charts and dashboards you explore in the product. For scheduled or on-demand **Excel** and **PDF** outputs from report templates (including via [automations](/workflows/automation-system)), use [Reports](/data/reports) instead.
**Capabilities:**
* **Charts** — Seven types (bar, line, pie, donut, single bar, single line, single value) for comparisons, trends, and KPIs
* **Aggregations** — Count, sum, average, min/max, percentages, and grouping; use [Calculations](/data/calculations) for formulas before charting when logic is complex
* **Time analysis** — Intervals (daily through yearly), custom and relative ranges, and period comparisons where configured
* **Dashboards** — Chart widgets, metric-style layouts, sizing, refresh intervals, and summary-style groupings
## Chart types
| Type | Use for | Examples |
| ---------------------------- | --------------------------------- | -------------------------------------------------------- |
| **Bar** | Categories and distributions | Sales by region, tickets by priority, volume by channel |
| **Line** | Trends over time | Recurring revenue by month, satisfaction over quarters |
| **Pie / donut** | Parts of a whole | Pipeline by stage, budget split, share by product |
| **Single bar / single line** | One metric or a simple comparison | Period vs. target, single KPI trend |
| **Single value** | One number with context | Active customers, monthly revenue, response-time average |
## Create and Configure Charts
1. **Open the target view** — Table or dashboard where the chart should appear.
2. **Choose a chart type** — Match the type to the comparison or trend you need.
3. **Select the data source** — Fields and metrics from connected data (including CloudLinks where available).
4. **Set aggregation** — Count, sum, average, min/max, or grouped rollups as supported for that source.
5. **Apply filters** — Narrow rows with saved or ad hoc filters, including combined conditions where supported.
6. **Adjust display** — Titles, colors, legends, and axis formatting.
### Aggregations
* **Count** — Total rows, distinct values, or conditional counts
* **Mathematical** — Sum, average, minimum, maximum for numeric fields
* **Advanced** — Include other values as a single aggregated group; percentage calculations for proportional analysis; custom calculations using field combinations
For logic beyond basic aggregations, define values with [Calculations](/data/calculations) first, then chart the result.
### Time ranges and date grouping
* **Intervals** — Daily, weekly, monthly, quarterly, or yearly (as offered for the field).
* **Ranges** — Fixed date spans, relative windows (for example last 30 days or this quarter), and rolling periods where supported.
* **Date Grouping** — By calendar unit or field; compare periods (for example month over month or year over year) when that option exists.
## Dashboards and widgets
* **Chart widgets** — Place charts on dashboard layouts with size and position controls.
* **Metric-style widgets** — Single values with optional trend indicators and threshold coloring where configured.
* **Summary layouts** — Multiple related metrics in one area for role-specific views (for example executive or department summaries).
### Real-time data updates
Analytics refresh as underlying data changes. Charts update when source data changes through live data sync. You can configure scheduled refresh intervals for automatic updates, run manual refresh when you need an immediate read, and use change indicators for visual cues that data has been updated. Set refresh frequency in line with how volatile your data is so dashboards stay current without overloading slow queries.
## Filters, interaction, and layout
### Filters
**Dynamic filtering:** Apply filters to narrow charts to specific data subsets; reuse saved filters for consistent analysis; combine conditions with AND/OR logic where supported.
**Segmentation:** Segment by categories, status, or custom field values; compare metrics across segments; use cohort-style breakdowns for customer behavior where available.
**Conditional display:** Show or hide chart elements based on data values; apply conditional formatting for threshold alerts; build legends that respond to which data is present where configured.
### Interaction
**Chart interaction:** Click chart elements to drill into underlying detail where enabled; hover for extra context and exact values; zoom time-series views where available.
**Export and sharing:** Export charts as images for presentations and reports; download the underlying tabular data for analysis outside Elementum; share chart configurations with collaborators when your permissions allow.
### Layout
**Visual styling:** Set chart titles and descriptions; choose color schemes for brand consistency; adjust legend placement and formatting; customize axis labels and number formats.
**Placement and display:** Control chart size and position within dashboards; use layouts that adapt for mobile viewing where supported; arrange charts in grid layouts for organized dashboard views.
## Example configurations
Use these as templates; rename sources and fields to match your App.
* Line chart — Sum of deal value by month, filter status = Closed Won (revenue trend).
* Donut chart — Count of opportunities by stage (pipeline mix).
* Line chart — Average survey rating by week.
* Bar chart — Count of tickets by priority.
* Single value — Average completion time on Task records, filter by team.
* Pie chart — Sum of hours by project type.
* Bar chart — Sum of amount by budget vs. actual category.
* Single bar — Sum of expenses by department.
## Performance
* Prefer shorter time windows, heavier aggregation, and filters so each chart returns fewer points.
* Align refresh frequency with how often the data changes; use manual refresh for rarely viewed boards.
* Group and filter on fields that are practical for your data source (indexed or selective where possible).
* Move heavy logic into [Calculations](/data/calculations) instead of pushing complex expressions into the chart query alone.
Very large result sets or deep aggregations can slow dashboards. Favor focused charts and summary widgets for broad exploration.
## Design and metric hygiene
* **Clarity** — Titles that state the measure and period; chart type that matches the question; limit color categories to a small set; show units on axes and labels.
* **Layout** — Important metrics first; group related charts; keep styling consistent across a dashboard.
* **Definitions** — Document what each metric means, reuse the same calculation for the same KPI, and review definitions when fields change.
* **Access** — Apply permissions appropriate to sensitive metrics; align dashboard access with data access policies.
## Work with other Elementum features
* **Tables** — Charts can follow table filters and table calculations where wired together; table updates flow to chart data.
* **Automations** — Automations react to [record and system events](/workflows/automation-system), not to chart widgets directly. Use data changes (for example field thresholds on underlying records) or schedules to drive notifications, [reports](/workflows/automation-actions-reference), and workflows that relate to the same metrics you chart.
* **Flows and approvals** — Track duration, volume, and outcomes with charts to spot bottlenecks in [approval](/workflows/approval-processes) and other processes.
* **Data mining** — Chart quality or volume metrics to monitor [data mining](/data/data-mining) outcomes and source health where you store those values in Elements or Tables.
# Calculations
Source: https://docs.elementum.io/data/calculations
Build formulas to transform values, aggregate related records, and evaluate conditions—in automations, layouts, and anywhere calculations are supported.
Calculations let you combine fields, operators, and functions to derive values from your data. Use them in automations, layouts, and data processing when you need totals, text formatting, date math, or conditional logic.
When a formula isn't behaving as expected, ask the [AI Docs Assistant](/support/resources#ai-docs-assistant) to help
troubleshoot your calculation or Execute Script action. Describe your input, the result you want, and where the value
is used (for example, an Update Record field or a Repeat for Each loop), and it will compose or debug the expression
from documented functions.
## Where to use calculations
Calculations can be configured in several places in Elementum:
* **Automations** — Use the **Run Calculation** action to evaluate expressions from triggers or previous actions (see [Automation actions reference](/workflows/automation-actions-reference)).
* **Element layouts** — Add formulas to derive values on records.
* **Calculated columns in tables** — For example, `Total = Quantity × Price` (see [Tables](/data/tables)).
* **Reports** — Add formulas to Excel reports (see [Reports](/data/reports)).
Get started with basic calculations
Complete list of all available functions
Real-world calculation scenarios
Common issues and solutions
## Quick Start
New to calculations? Start with these patterns for totals, text, dates, and conditional logic.
### Most Used Functions
```javascript theme={null}
// Sum all invoice amounts
SUM(INVOICES."Amount")
// Average customer rating
AVERAGE(REVIEWS."Rating")
// Count completed tasks
COUNT(TASKS."ID")
```
```javascript theme={null}
// Create full name
CONCAT(CUSTOMERS."FirstName", ' ', CUSTOMERS."LastName")
// Standardize email format
LOWER(CUSTOMERS."Email")
// Format product codes
UPPER(PRODUCTS."SKU")
```
```javascript theme={null}
// Current date and time (there is no TODAY() function — use NOW())
NOW()
// Add 5 days to today
DATEADD(DAY, 5, NOW())
// Subtract 1 month from a due date
DATEADD(MONTH, -1, TASKS."DueDate")
// Days since order placed
DATEDIF(ORDERS."OrderDate", NOW(), 'D')
// Convert text to date (input must be YYYY-MM-DD)
DATEVALUE(CUSTOMERS."SignupDate")
```
```javascript theme={null}
// Customer status based on spending
IF(CUSTOMERS."TotalSpent" > 1000, 'VIP', 'Standard')
// Eligible for discount
AND(CUSTOMERS."TotalSpent" > 500, CUSTOMERS."MembershipLevel" = 'Gold')
// Nested conditions for customer tiers
IF(CUSTOMERS."TotalSpent" > 5000, 'Enterprise',
IF(CUSTOMERS."TotalSpent" > 1000, 'Premium', 'Standard'))
```
### Common date recipes
The most-asked date questions, with the exact syntax to use.
#### Get today's date
Elementum does not have a `TODAY()` function. Use `NOW()` for the current date and time, and wrap it in `DATE()` if you need just the date portion:
```javascript theme={null}
// Current date and time
NOW()
// Just today's date (no time component)
DATE(YEAR(NOW()), MONTH(NOW()), DAY(NOW()))
```
#### Get the current date and time
`NOW()` returns the current timestamp. It takes no arguments:
```javascript theme={null}
NOW()
```
Use it inside other functions to derive values against the current moment:
```javascript theme={null}
// Days since a stored date
DATEDIF(ORDERS."OrderDate", NOW(), 'D')
// Current year for reporting
YEAR(NOW())
```
#### Add days to a date
Use [`DATEADD(unit, value, date)`](#dateadd---add-or-subtract-time-from-a-date) with an unquoted unit token like `DAY` or `MONTH`:
```javascript theme={null}
// Add 5 days to today
DATEADD(DAY, 5, NOW())
// Expected ship-by, 30 days after order
DATEADD(DAY, 30, ORDERS."OrderDate")
```
#### Subtract days from a date
Pass a negative value to `DATEADD`:
```javascript theme={null}
// 7 days ago
DATEADD(DAY, -7, NOW())
// One month before a due date
DATEADD(MONTH, -1, TASKS."DueDate")
```
`DATEADD` handles month and year rollovers automatically — adding days across the end of a month advances into the next month as expected.
## Business Examples
### Calculate Monthly Sales Performance
```javascript theme={null}
// Total monthly revenue
SUM(ORDERS."Amount")
// Average order value
AVERAGE(ORDERS."Amount")
// Top performing month
MAX_AGGREGATE(MONTHLY_SALES."Revenue")
// Sales growth percentage
ROUND((THIS_MONTH."Revenue" - LAST_MONTH."Revenue") / LAST_MONTH."Revenue" * 100, 2)
// High-value customer identification
IF(CUSTOMERS."TotalSpent" > 5000, 'Enterprise',
IF(CUSTOMERS."TotalSpent" > 1000, 'Premium', 'Standard'))
```
### Customer Insights & Segmentation
```javascript theme={null}
// Customer full name
CONCAT(CUSTOMERS."FirstName", ' ', CUSTOMERS."LastName")
// Days since last purchase
DATEDIF(CUSTOMERS."LastPurchaseDate", NOW(), 'D')
// Customer lifetime value
SUM(ORDERS."Amount")
// Customer age
DATEDIF(CUSTOMERS."BirthDate", NOW(), 'Y')
// At-risk customer flag
IF(DATEDIF(CUSTOMERS."LastPurchaseDate", NOW(), 'D') > 90, 'At Risk', 'Active')
```
### Inventory Management
```javascript theme={null}
// Stock level status
IF(PRODUCTS."StockLevel" < PRODUCTS."ReorderPoint", 'Low Stock', 'OK')
// Days of inventory remaining
ROUND(PRODUCTS."StockLevel" / AVERAGE(DAILY_SALES."Quantity"), 0)
// Total inventory value
SUM(PRODUCTS."StockLevel" * PRODUCTS."Cost")
// Product performance score
ROUND((PRODUCTS."Revenue" / PRODUCTS."Cost") * 100, 2)
// Reorder recommendation
IF(PRODUCTS."StockLevel" < PRODUCTS."ReorderPoint",
CONCAT('Reorder ', PRODUCTS."ReorderQuantity", ' units'),
'Stock OK')
```
## Function Reference
Functions are organized by category. Use the search function (Ctrl/Cmd + K) to quickly find specific functions.
### Logical Functions
Tests multiple conditions and returns TRUE only if all are TRUE.
**Syntax:** `AND(condition1, condition2, ...)`
**Business Example:**
```javascript theme={null}
// Check if customer is eligible for discount
AND(CUSTOMERS."TotalSpent" > 500, CUSTOMERS."MembershipLevel" = 'Gold')
// Validate complete order
AND(ORDERS."PaymentStatus" = 'Paid', ORDERS."ShippingAddress" != '')
// Employee bonus eligibility
AND(EMPLOYEES."SalesTarget" <= EMPLOYEES."ActualSales", EMPLOYEES."Tenure" > 1)
```
**Arguments:**
* `condition1, condition2, ...`: Logical expressions that evaluate to TRUE/FALSE
If any condition is blank, the result will be blank.
Tests multiple conditions and returns TRUE if any are TRUE.
**Syntax:** `OR(condition1, condition2, ...)`
**Business Example:**
```javascript theme={null}
// Flag tickets that are urgent or overdue
OR(TICKETS."Priority" = 'Urgent', TICKETS."DaysOpen" > 7)
// Identify customers worth re-engaging
OR(CUSTOMERS."DaysSinceLastPurchase" > 90, CUSTOMERS."OpenTickets" > 0)
// Apply discount when any qualifying condition is met
OR(ORDERS."DiscountCode" != '', ORDERS."LoyaltyTier" = 'Gold')
```
**Arguments:**
* `condition1, condition2, ...`: Logical expressions that evaluate to TRUE/FALSE
If any condition is blank, the result will be blank.
Returns different values based on a condition.
**Syntax:** `IF(condition, value_if_true, value_if_false)`
**Business Example:**
```javascript theme={null}
// Customer status based on spending
IF(CUSTOMERS."TotalSpent" > 1000, 'VIP', 'Standard')
// Shipping cost calculation
IF(ORDERS."Amount" > 100, 0, 9.99)
// Performance rating
IF(EMPLOYEES."SalesTarget" <= EMPLOYEES."ActualSales", 'Exceeded', 'Below Target')
// Nested conditions for customer tiers
IF(CUSTOMERS."TotalSpent" > 5000, 'Enterprise',
IF(CUSTOMERS."TotalSpent" > 1000, 'Premium', 'Standard'))
```
**Arguments:**
* `condition`: Logical expression
* `value_if_true`: Value returned when condition is TRUE
* `value_if_false`: Value returned when condition is FALSE
For more than two outcomes, use `IFS` instead of nesting multiple `IF` statements—it's flatter and easier to read.
Evaluates multiple conditions in order and returns the value corresponding to the first condition that is TRUE.
**Syntax:** `IFS(condition1, value1, condition2, value2, ...)`
**Business Example:**
```javascript theme={null}
// Customer tier based on spending
IFS(
CUSTOMERS."TotalSpent" > 5000, 'Enterprise',
CUSTOMERS."TotalSpent" > 1000, 'Premium',
TRUE(), 'Standard'
)
// Order priority based on age
IFS(
ORDERS."DaysOpen" > 14, 'Critical',
ORDERS."DaysOpen" > 7, 'High',
ORDERS."DaysOpen" > 3, 'Medium',
TRUE(), 'Low'
)
// Shipping band based on order amount
IFS(
ORDERS."Amount" >= 250, 'Free',
ORDERS."Amount" >= 100, 'Discounted',
TRUE(), 'Standard'
)
// Evaluation walkthrough — returned value depends on field values
IFS(
BAT."Number" = 0, '0',
BAT."Number" > 4, '>4',
BAT."Status" = 'New', 'New'
)
// Number=0, Status='New' → '0' (first condition matches)
// Number=1, Status='New' → 'New' (skips 0 and >4, matches Status)
// Number=5, Status='New' → '>4' (matches >4 before Status is checked)
// Number=null, Status='New' → 'New' (null comparisons don't match)
// Number=null, Status='Old' → blank (no condition matches)
```
**Arguments:**
* `condition1, condition2, ...`: Logical expressions evaluated in order
* `value1, value2, ...`: Value returned for the corresponding condition when it is the first to evaluate to TRUE
When no condition is TRUE, `IFS` returns blank. Include a final `TRUE()` condition as a catch-all to guarantee a value is always returned.
### Numeric Functions
Calculates the total sum of values in a related field.
**Syntax:** `SUM(related_field)`
**Business Example:**
```javascript theme={null}
// Total revenue from all orders
SUM(ORDERS."Amount")
// Total hours worked by employee
SUM(TIMESHEETS."Hours")
// Total inventory value
SUM(PRODUCTS."StockLevel" * PRODUCTS."UnitCost")
// Customer lifetime value
SUM(CUSTOMER_ORDERS."Amount")
```
**Arguments:**
* `related_field`: Field from related records to sum
This function only works with related fields, not individual values. For adding individual values, use the '+' operator.
Returns the numerical average of values in a related field.
**Syntax:** `AVERAGE(related_field)`
**Business Example:**
```javascript theme={null}
// Average customer rating
AVERAGE(REVIEWS."Rating")
// Average order value
AVERAGE(ORDERS."Amount")
// Average employee salary by department
AVERAGE(EMPLOYEES."Salary")
// Average project completion time
AVERAGE(PROJECTS."CompletionDays")
```
**Arguments:**
* `related_field`: Field from related records to average
Blank values are automatically excluded from the calculation.
Counts the number of non-null values in a related field.
**Syntax:** `COUNT(related_field)`
**Business Example:**
```javascript theme={null}
// Number of orders placed
COUNT(ORDERS."ID")
// Number of completed tasks
COUNT(TASKS."CompletedDate")
// Number of active customers
COUNT(CUSTOMERS."LastLoginDate")
// Number of products in stock
COUNT(PRODUCTS."StockLevel")
```
**Arguments:**
* `related_field`: Field from related records to count
Use the ID field to count total records, or use a specific field to count only non-null values.
Counts non-null values in a related field that meet a specified condition.
**Syntax:** `COUNTIF(related_field, criterion)`
**Business Example:**
```javascript theme={null}
// Count high-value orders
COUNTIF(ORDERS."Amount", '>1000')
// Count 5-star reviews
COUNTIF(REVIEWS."Rating", '=5')
// Count overdue tasks
COUNTIF(TASKS."DueDate", '<' + TEXT(NOW()))
// Count products with low stock
COUNTIF(PRODUCTS."StockLevel", '<10')
```
**Arguments:**
* `related_field`: Field from related records to count
* `criterion`: Condition to meet (supports comparison operators)
Alternative syntax: `SUM(IF(RELATED."Field" = 'Paid', 1, 0))`
Counts the number of unique values in a related field.
**Syntax:** `COUNTUNIQUE(related_field)`
**Business Example:**
```javascript theme={null}
// Number of unique customers
COUNTUNIQUE(ORDERS."CustomerID")
// Number of different product categories
COUNTUNIQUE(PRODUCTS."Category")
// Number of unique sales reps
COUNTUNIQUE(DEALS."SalesRep")
// Number of unique support ticket types
COUNTUNIQUE(TICKETS."Type")
```
**Arguments:**
* `related_field`: Field from related records to count unique values
Null values are excluded from the count.
Returns the maximum value from a given set of values.
**Syntax:** `MAX(value1, value2, ...)`
**Business Example:**
```javascript theme={null}
// Highest of three scores
MAX(PERFORMANCE."Q1Score", PERFORMANCE."Q2Score", PERFORMANCE."Q3Score")
// Maximum shipping cost between options
MAX(SHIPPING."Standard", SHIPPING."Express", SHIPPING."Overnight")
// Latest date from multiple fields
MAX(CUSTOMER."LastPurchase", CUSTOMER."LastContact", CUSTOMER."LastLogin")
```
**Arguments:**
* `value1, value2, ...`: Values to compare
Blank values are ignored. For aggregate calculations, use MAX\_AGGREGATE.
Finds the maximum value from a related field or calculation.
**Syntax:** `MAX_AGGREGATE(related_field)`
**Business Example:**
```javascript theme={null}
// Highest order amount
MAX_AGGREGATE(ORDERS."Amount")
// Best employee performance score
MAX_AGGREGATE(EMPLOYEES."PerformanceScore")
// Peak sales month
MAX_AGGREGATE(MONTHLY_SALES."Revenue")
// Highest customer satisfaction rating
MAX_AGGREGATE(SURVEYS."SatisfactionScore")
```
**Arguments:**
* `related_field`: Field from related records to find maximum
This is the aggregate version of MAX for related data.
Returns the minimum value from a given set of values.
**Syntax:** `MIN(value1, value2, ...)`
**Business Example:**
```javascript theme={null}
// Lowest of three prices
MIN(PRICING."Standard", PRICING."Discount", PRICING."Wholesale")
// Earliest date from multiple fields
MIN(PROJECT."StartDate", PROJECT."PlannedStart", PROJECT."ActualStart")
// Minimum required inventory
MIN(PRODUCT."SafetyStock", PRODUCT."ReorderPoint", 10)
```
**Arguments:**
* `value1, value2, ...`: Values to compare
Blank values are ignored. For aggregate calculations, use MIN\_AGGREGATE.
Finds the minimum value from a related field or calculation.
**Syntax:** `MIN_AGGREGATE(related_field)`
**Business Example:**
```javascript theme={null}
// Lowest order amount
MIN_AGGREGATE(ORDERS."Amount")
// Shortest project duration
MIN_AGGREGATE(PROJECTS."Duration")
// Lowest inventory level
MIN_AGGREGATE(PRODUCTS."StockLevel")
// Minimum customer age
MIN_AGGREGATE(CUSTOMERS."Age")
```
**Arguments:**
* `related_field`: Field from related records to find minimum
This is the aggregate version of MIN for related data.
Rounds a number to a specified number of decimal places.
**Syntax:** `ROUND(number, [decimal_places])`
**Business Example:**
```javascript theme={null}
// Round currency to 2 decimal places
ROUND(ORDERS."Amount", 2)
// Round percentage to whole number
ROUND(SALES."GrowthRate" * 100, 0)
// Round to nearest thousand
ROUND(REVENUE."Annual", -3)
// Round average rating
ROUND(AVERAGE(REVIEWS."Rating"), 1)
```
**Arguments:**
* `number`: Number to round
* `decimal_places`: \[OPTIONAL] Number of decimal places (default: 0)
Negative decimal\_places rounds to left of decimal point (e.g., -1 rounds to tens).
Calculates the standard deviation of a related field.
**Syntax:** `STDEV(related_field)`
**Business Example:**
```javascript theme={null}
// Variability in order amounts
STDEV(ORDERS."Amount")
// Consistency of employee performance
STDEV(EMPLOYEES."PerformanceScore")
// Product rating consistency
STDEV(REVIEWS."Rating")
// Sales performance variability
STDEV(SALES_REPS."MonthlySales")
```
**Arguments:**
* `related_field`: Field from related records to calculate standard deviation
Standard deviation measures how spread out values are from the average.
Returns the sum of values in a field that meet a specified condition.
**Syntax:** `SUMIF(related_field, criterion)`
**Business Example:**
```javascript theme={null}
// Revenue from high-value orders
SUMIF(ORDERS."Amount", '>1000')
// Total hours for completed tasks
SUMIF(TASKS."Hours", TASKS."Status" = 'Completed')
// Revenue from premium customers
SUMIF(ORDERS."Amount", CUSTOMERS."Tier" = 'Premium')
// Sales from specific region
SUMIF(SALES."Amount", SALES."Region" = 'North')
```
**Arguments:**
* `related_field`: Field from related records to sum
* `criterion`: Condition values must meet
Use operators like greater than, less than, greater than or equal to, less than or equal to, and equal to in your criteria.
### Date and Time Functions
Returns the current date and time.
**Syntax:** `NOW()`
**Business Example:**
```javascript theme={null}
// Timestamp for new records
NOW()
// Days since order placed
DATEDIF(ORDERS."OrderDate", NOW(), 'D')
// Current year for reporting
YEAR(NOW())
// Age calculation
DATEDIF(CUSTOMERS."BirthDate", NOW(), 'Y')
```
This function takes no arguments and always returns the current moment. There is no separate `TODAY()` function — use `NOW()` and, if you need date-only, wrap it in `DATE(YEAR(NOW()), MONTH(NOW()), DAY(NOW()))`.
Adds a value in a given unit to a date or datetime. Pass a negative value to subtract.
**Syntax:** `DATEADD(unit, value, date)`
**Business Example:**
```javascript theme={null}
// Add 7 days to today
DATEADD(DAY, 7, NOW())
// Subtract 1 month from a due date
DATEADD(MONTH, -1, TASKS."DueDate")
// Expected ship-by, 30 days after an order
DATEADD(DAY, 30, ORDERS."OrderDate")
// Ninety-day follow-up after a customer's last purchase
DATEADD(DAY, 90, CUSTOMERS."LastPurchaseDate")
```
**Arguments:**
* `unit`: Date/time unit token — for example, `DAY` or `MONTH`. Passed unquoted.
* `value`: Number of units to add. Use a negative number to subtract.
* `date`: Starting date or datetime
`DATEADD` handles month and year boundaries automatically — adding days across the end of a month or year rolls forward as expected. This is the recommended way to shift a date by a fixed amount.
Returns a date value based on provided year, month, and day.
**Syntax:** `DATE(year, month, day)`
**Business Example:**
```javascript theme={null}
// Create fiscal year start date
DATE(YEAR(NOW()), 4, 1)
// Build date from separate fields
DATE(ORDERS."Year", ORDERS."Month", ORDERS."Day")
// Create quarter end date
DATE(2024, 3, 31)
// Generate report date
DATE(REPORTS."ReportYear", REPORTS."ReportMonth", 1)
// Extract the date portion from a datetime field
// (use this to populate a Date field from a DateTime field
// in an Update Record Fields automation action)
DATE(
YEAR(ORDERS."OrderDateTime"),
MONTH(ORDERS."OrderDateTime"),
DAY(ORDERS."OrderDateTime")
)
```
**Arguments:**
* `year`: Four-digit year
* `month`: Month (1-12)
* `day`: Day of month (1-31)
Values exceeding normal ranges automatically adjust (e.g., month 13 becomes January of next year).
To shift a date by a fixed amount (add or subtract days, months, and so on), use [`DATEADD`](#dateadd---add-or-subtract-time-from-a-date) rather than building a new `DATE` with adjusted components.
There is no direct "datetime to date" conversion option in calculations or in the **Update Record Fields** automation action. To populate a Date field from a DateTime field, wrap the datetime value in `DATE(YEAR(...), MONTH(...), DAY(...))` as shown above.
Returns a datetime value in the company's timezone.
**Syntax:** `DATETIME(year, month, day, hour, minute, second)`
**Business Example:**
```javascript theme={null}
// Create meeting start time
DATETIME(2024, 3, 15, 9, 30, 0)
// Build timestamp from fields
DATETIME(EVENTS."Year", EVENTS."Month", EVENTS."Day", EVENTS."Hour", 0, 0)
// Create deadline
DATETIME(TASKS."DueYear", TASKS."DueMonth", TASKS."DueDay", 23, 59, 59)
// Sentinel "max" datetime — 12/31/9999 11:59 PM
DATETIME(9999, 12, 31, 23, 59, 0)
// Sentinel "min" datetime — 1/1/1901 12:00 AM (midnight)
DATETIME(1901, 1, 1, 0, 0, 0)
// Add 1 hour to now
DATETIME(YEAR(NOW()), MONTH(NOW()), DAY(NOW()), HOUR(NOW()) + 1, MINUTE(NOW()), 0)
```
**Arguments:**
* `year`: Four-digit year
* `month`: Month (1-12)
* `day`: Day of month (1-31)
* `hour`: Hour (0-23)
* `minute`: Minute (0-59)
* `second`: Second (0-59)
Time is set in your company's timezone.
Calculates the difference between two dates in specified units.
**Syntax:** `DATEDIF(start_date, end_date, unit)`
**Business Example:**
```javascript theme={null}
// Customer age
DATEDIF(CUSTOMERS."BirthDate", NOW(), 'Y')
// Days since last purchase
DATEDIF(CUSTOMERS."LastPurchaseDate", NOW(), 'D')
// Project duration in months
DATEDIF(PROJECTS."StartDate", PROJECTS."EndDate", 'M')
// Employee tenure
DATEDIF(EMPLOYEES."HireDate", NOW(), 'Y')
```
**Arguments:**
* `start_date`: Beginning date
* `end_date`: End date
* `unit`: 'Y' for years, 'M' for months, 'D' for days
Returns negative values if start\_date is after end\_date.
Truncates a datetime to a specified unit.
**Syntax:** `DATETIME_TRUNC(datetime, unit)`
**Business Example:**
```javascript theme={null}
// Start of month for reporting
DATETIME_TRUNC(ORDERS."OrderDate", 'MONTH')
// Start of day for daily summaries
DATETIME_TRUNC(EVENTS."EventTime", 'DAY')
// Start of quarter
DATETIME_TRUNC(SALES."SaleDate", 'QUARTER')
// Start of year
DATETIME_TRUNC(EMPLOYEES."HireDate", 'YEAR')
```
**Arguments:**
* `datetime`: Datetime to truncate
* `unit`: YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND
Useful for grouping data by time periods.
Converts text date value into a DATE object.
**Syntax:** `DATEVALUE(text_date)`
**Business Example:**
```javascript theme={null}
// Convert imported date text
DATEVALUE(IMPORTS."DateString")
// Parse date from external system
DATEVALUE(EXTERNAL."FormattedDate")
// Convert user-entered date
DATEVALUE(FORMS."SubmissionDate")
```
**Arguments:**
* `text_date`: Text representation of a date
Returns null if the text cannot be parsed as a date.
**Accepted format:** input must be in `YYYY-MM-DD` form — date only, no time component. Other formats (`MM/DD/YYYY`, written-out months, ISO 8601 strings with a time component) are not parsed and return blank. See [Calculations troubleshooting](/data/calculations-troubleshooting#datevalue-only-accepts-year-month-day-input) for patterns.
Returns the day of the month (1-31) from a date.
**Syntax:** `DAY(date)`
**Business Example:**
```javascript theme={null}
// Extract day for daily reports
DAY(ORDERS."OrderDate")
// Get payment day
DAY(INVOICES."DueDate")
// Extract birth day
DAY(CUSTOMERS."BirthDate")
```
**Arguments:**
* `date`: Date to extract day from
Returns a number between 1 and 31.
Returns the month (1-12) from a date.
**Syntax:** `MONTH(date)`
**Business Example:**
```javascript theme={null}
// Extract month for monthly reports
MONTH(ORDERS."OrderDate")
// Get birth month
MONTH(CUSTOMERS."BirthDate")
// Extract fiscal month
MONTH(TRANSACTIONS."TransactionDate")
```
**Arguments:**
* `date`: Date to extract month from
Returns a number between 1 (January) and 12 (December).
Returns the year from a date.
**Syntax:** `YEAR(date)`
**Business Example:**
```javascript theme={null}
// Extract year for annual reports
YEAR(ORDERS."OrderDate")
// Get hire year
YEAR(EMPLOYEES."HireDate")
// Extract birth year
YEAR(CUSTOMERS."BirthDate")
```
**Arguments:**
* `date`: Date to extract year from
Returns a four-digit year number.
Returns the hour (0-23) from a datetime.
**Syntax:** `HOUR(datetime)`
**Business Example:**
```javascript theme={null}
// Extract hour for time-based analysis
HOUR(ORDERS."OrderTime")
// Get meeting hour
HOUR(MEETINGS."StartTime")
// Extract login hour
HOUR(USERS."LastLogin")
```
**Arguments:**
* `datetime`: Datetime to extract hour from
Returns a number between 0 (midnight) and 23 (11 PM).
Returns the minute (0-59) from a datetime.
**Syntax:** `MINUTE(datetime)`
**Business Example:**
```javascript theme={null}
// Extract minute for precise timing
MINUTE(MEETINGS."StartTime")
// Get appointment minute
MINUTE(APPOINTMENTS."ScheduledTime")
// Extract timestamp minute
MINUTE(EVENTS."EventTime")
```
**Arguments:**
* `datetime`: Datetime to extract minute from
Returns a number between 0 and 59.
Returns the second (0-59) from a datetime.
**Syntax:** `SECOND(datetime)`
**Business Example:**
```javascript theme={null}
// Extract second for precise timing
SECOND(TRANSACTIONS."Timestamp")
// Get event second
SECOND(EVENTS."EventTime")
// Extract log second
SECOND(LOGS."LogTime")
```
**Arguments:**
* `datetime`: Datetime to extract second from
Returns a number between 0 and 59.
Returns the day of the week (1-7) for a date.
**Syntax:** `WEEKDAY(date, [type])`
**Business Example:**
```javascript theme={null}
// Get weekday for scheduling
WEEKDAY(MEETINGS."MeetingDate", 2)
// Analyze sales by day of week
WEEKDAY(SALES."SaleDate")
// Check if order was placed on a specific day
WEEKDAY(ORDERS."OrderDate") = 1
```
**Arguments:**
* `date`: Date to get weekday from
* `type`: \[OPTIONAL] 1=Sun-Sat (1-7), 2=Mon-Sun (1-7), 3=Mon-Sun (0-6)
Type 1 (default): Sunday=1, Monday=2, ..., Saturday=7
### Text Functions
Joins multiple text values into a single string.
**Syntax:** `CONCAT(text1, text2, ...)`
**Business Example:**
```javascript theme={null}
// Customer full name
CONCAT(CUSTOMERS."FirstName", ' ', CUSTOMERS."LastName")
// Product description
CONCAT(PRODUCTS."Brand", ' - ', PRODUCTS."Model", ' (', PRODUCTS."Color", ')')
// Order summary
CONCAT('Order #', ORDERS."OrderNumber", ' - ', ORDERS."Status")
// Address formatting
CONCAT(CUSTOMERS."Street", ', ', CUSTOMERS."City", ', ', CUSTOMERS."State")
```
**Arguments:**
* `text1, text2, ...`: Text values to join together
Various field types are automatically converted to text for concatenation.
`CONCAT` is the only way to join text values. **The `+` operator is for numeric addition only** — it does not concatenate strings, even though it does in some other languages. See [Calculations troubleshooting](/data/calculations-troubleshooting#concatenating-strings).
Calculations cannot insert a raw newline inside `CONCAT` — there is no `CHAR` or `CHR` function, and `'\n'` is treated as two literal characters. For multi-line output, build the string in an [Execute Script](/workflows/automation-actions-reference#data-actions) action. See [Adding a newline between concatenated values](/data/calculations-troubleshooting#adding-a-newline-between-concatenated-values).
Converts text to uppercase letters.
**Syntax:** `UPPER(text)`
**Business Example:**
```javascript theme={null}
// Standardize product codes
UPPER(PRODUCTS."SKU")
// Format state abbreviations
UPPER(CUSTOMERS."State")
// Consistent department names
UPPER(EMPLOYEES."Department")
// Normalize country codes
UPPER(ADDRESSES."CountryCode")
```
**Arguments:**
* `text`: Text to convert to uppercase
Useful for standardizing data entry and comparisons.
Converts text to lowercase letters.
**Syntax:** `LOWER(text)`
**Business Example:**
```javascript theme={null}
// Standardize email addresses
LOWER(CUSTOMERS."Email")
// Consistent username format
LOWER(USERS."Username")
// Normalize search terms
LOWER(SEARCH."Query")
// Standardize domain names
LOWER(WEBSITES."Domain")
```
**Arguments:**
* `text`: Text to convert to lowercase
Use when you need case-insensitive comparisons or normalized text (for example, email addresses).
Extracts characters from the beginning of a string.
**Syntax:** `LEFT(text, number_of_characters)`
**Business Example:**
```javascript theme={null}
// Extract first 3 characters of product code
LEFT(PRODUCTS."SKU", 3)
// Get first initial
LEFT(CUSTOMERS."FirstName", 1)
// Extract area code from phone
LEFT(CUSTOMERS."Phone", 3)
// Get first part of order number
LEFT(ORDERS."OrderNumber", 4)
```
**Arguments:**
* `text`: String to extract from
* `number_of_characters`: Number of characters to extract
Returns the entire string if requested length exceeds string length.
Extracts characters from the end of a string.
**Syntax:** `RIGHT(text, number_of_characters)`
**Business Example:**
```javascript theme={null}
// Extract last 4 digits of credit card
RIGHT(PAYMENTS."CardNumber", 4)
// Get file extension
RIGHT(ATTACHMENTS."FileName", 4)
// Extract year from date string
RIGHT(RECORDS."DateString", 4)
// Get last part of account number
RIGHT(ACCOUNTS."AccountNumber", 6)
```
**Arguments:**
* `text`: String to extract from
* `number_of_characters`: Number of characters to extract
Returns the entire string if requested length exceeds string length.
Extracts substring from specified position.
**Syntax:** `MID(text, start_position, number_of_characters)`
**Business Example:**
```javascript theme={null}
// Extract middle digits from account number
MID(ACCOUNTS."AccountNumber", 5, 4)
// Get month from date string (MM/DD/YYYY)
MID(RECORDS."DateString", 4, 2)
// Extract product category from code
MID(PRODUCTS."SKU", 3, 2)
```
**Arguments:**
* `text`: String to extract from
* `start_position`: Starting position (1-based)
* `number_of_characters`: Number of characters to extract
Position counting starts at 1, not 0.
Returns position of first case-sensitive substring match.
**Syntax:** `FIND(search_text, text_to_search, [start_position])`
**Business Example:**
```javascript theme={null}
// Find @ symbol in email
FIND('@', CUSTOMERS."Email")
// Find dash in product code
FIND('-', PRODUCTS."SKU")
// Find space in full name
FIND(' ', CUSTOMERS."FullName")
```
**Arguments:**
* `search_text`: Text to find
* `text_to_search`: Text to search within
* `start_position`: \[OPTIONAL] Starting position for search
Returns 0 if text not found. Case-sensitive search.
Returns position of first case-insensitive substring match.
**Syntax:** `SEARCH(search_text, text_to_search, [start_position])`
**Business Example:**
```javascript theme={null}
// Find 'premium' in product name (any case)
SEARCH('premium', PRODUCTS."Name")
// Find 'manager' in job title
SEARCH('manager', EMPLOYEES."JobTitle")
// Find 'urgent' in support ticket
SEARCH('urgent', TICKETS."Subject")
```
**Arguments:**
* `search_text`: Text to find
* `text_to_search`: Text to search within
* `start_position`: \[OPTIONAL] Starting position for search
Returns 0 if text not found. Case-insensitive search.
Replaces text occurrences in a string.
**Syntax:** `SUBSTITUTE(text, old_text, new_text)`
**Business Example:**
```javascript theme={null}
// Replace dashes with spaces in product codes
SUBSTITUTE(PRODUCTS."SKU", '-', ' ')
// Replace old company name in addresses
SUBSTITUTE(CUSTOMERS."Address", 'Old Corp', 'New Corp')
// Clean phone number formatting
SUBSTITUTE(CUSTOMERS."Phone", '(', '')
```
**Arguments:**
* `text`: Original text
* `old_text`: Text to replace
* `new_text`: Replacement text
Replaces ALL occurrences of old\_text with new\_text.
Removes whitespace from the beginning and end of a string.
**Syntax:** `TRIM(text)`
**Business Example:**
```javascript theme={null}
// Clean up imported customer names
TRIM(IMPORTS."CustomerName")
// Normalize email entries before comparison
TRIM(LOWER(CUSTOMERS."Email"))
// Remove padding from form input
TRIM(FORMS."CommentField")
```
**Arguments:**
* `text`: Text to trim
Useful for cleaning imported data or user input that may contain accidental whitespace. Spaces between words are preserved.
Returns the number of characters in a string.
**Syntax:** `LEN(text)`
**Business Example:**
```javascript theme={null}
// Check if password meets minimum length
LEN(USERS."Password") >= 8
// Validate phone number length
LEN(CUSTOMERS."Phone") = 10
// Check product code format
LEN(PRODUCTS."SKU") = 8
// Validate input length
LEN(FORMS."Description") <= 500
```
**Arguments:**
* `text`: Text to measure
Useful for data validation and formatting checks.
Concatenates multiple related strings with a delimiter.
**Syntax:** `STRING_AGG(related_field, delimiter)`
**Business Example:**
```javascript theme={null}
// List all order items
STRING_AGG(ORDER_ITEMS."ProductName", ', ')
// Create skill list for employee
STRING_AGG(EMPLOYEE_SKILLS."SkillName", ', ')
// List customer tags
STRING_AGG(CUSTOMER_TAGS."TagName", ', ')
```
**Arguments:**
* `related_field`: Field from related records to concatenate
* `delimiter`: Text to put between each value
Builds a single text value from related records, separated by the delimiter you choose.
**Accepted input types:** single-value text only, supplied via a related-field aggregation. Passing a `MULTI_PICKLIST` field or an array-shaped value raises `Invalid Type Error`. There is no supported way to aggregate a multi-picklist field directly in a calculation — do the work in [Execute Script](/data/multi-value-fields#aggregating-into-a-comma-separated-string) instead. See also [Calculations troubleshooting](/data/calculations-troubleshooting#aggregating-a-multi-picklist-into-a-delimited-string).
Concatenates unique values from a related field.
**Syntax:** `STRING_AGG_UNIQUE(related_field, delimiter)`
**Business Example:**
```javascript theme={null}
// List unique product categories
STRING_AGG_UNIQUE(ORDER_ITEMS."Category", ', ')
// List unique customer locations
STRING_AGG_UNIQUE(CUSTOMERS."City", ', ')
// List unique skills
STRING_AGG_UNIQUE(EMPLOYEE_SKILLS."SkillName", ', ')
```
**Arguments:**
* `related_field`: Field from related records to concatenate
* `delimiter`: Text to put between each value
Automatically removes duplicates before concatenating.
**Accepted input types:** single-value text only, supplied via a related-field aggregation. Passing a `MULTI_PICKLIST` field or an array-shaped value raises `Invalid Type Error`. There is no supported way to aggregate a multi-picklist field directly in a calculation — do the work in [Execute Script](/data/multi-value-fields#aggregating-into-a-comma-separated-string) instead. See also [Calculations troubleshooting](/data/calculations-troubleshooting#aggregating-a-multi-picklist-into-a-delimited-string).
Splits a string into an array of substrings using a delimiter.
**Syntax:** `SPLIT(text, delimiter)`
**Business Example:**
```javascript theme={null}
// Split a multi-value tag field
// If PRODUCTS."Colors" is 'Red|Green|Blue', returns ['Red', 'Green', 'Blue']
SPLIT(PRODUCTS."Colors", '|')
// Parse comma-separated labels
SPLIT(CUSTOMERS."Tags", ',')
// Break apart a structured order reference
SPLIT(ORDERS."Reference", '-')
```
**Arguments:**
* `text`: String to split
* `delimiter`: Character or string to split on
Returns an array of substrings. An empty delimiter (`''`) splits the text into individual characters. Returns blank if the input is blank.
`SPLIT` returns an array, but **selecting a single element from the result is not currently supported** in calculations. To extract "the part before the dash" or similar, use `LEFT`/`RIGHT`/`MID` combined with `FIND`/`SEARCH` instead — see [Calculations troubleshooting](/data/calculations-troubleshooting#split-returns-an-array-you-cant-index-into) for patterns.
Converts numbers or dates to text format.
**Syntax:** `TEXT(value)`
**Business Example:**
```javascript theme={null}
// Convert order amount to text
TEXT(ORDERS."Amount")
// Convert date to text
TEXT(ORDERS."OrderDate")
// Convert ID to text for concatenation
TEXT(CUSTOMERS."ID")
```
**Arguments:**
* `value`: Number or date to convert
Useful when you need to treat numbers as text for concatenation.
**`TEXT` does not accept a format string.** It returns the value's default text representation — there is no second argument to control output (no `'YYYY-MM-DD'`, `'MM/DD/YYYY'`, or similar). To produce a specific date or datetime string, extract the parts with `YEAR`, `MONTH`, `DAY`, `HOUR`, `MINUTE`, `SECOND` and assemble them with `CONCAT`:
```javascript theme={null}
// 'YYYY-MM-DD' from a date or datetime field
CONCAT(
TEXT(YEAR(ORDERS."OrderDate")), '-',
TEXT(MONTH(ORDERS."OrderDate")), '-',
TEXT(DAY(ORDERS."OrderDate"))
)
```
Pad single-digit months and days yourself if you need zero-padding (for example with `IF(MONTH(...) < 10, CONCAT('0', TEXT(MONTH(...))), TEXT(MONTH(...)))`).
Converts text to a number.
**Syntax:** `VALUE(text)`
**Business Example:**
```javascript theme={null}
// Convert text amount to number
VALUE(IMPORTS."AmountText")
// Convert text quantity to number
VALUE(FORMS."QuantityInput")
// Convert text ID to number
VALUE(EXTERNAL."IDString")
```
**Arguments:**
* `text`: Text to convert to number
Returns blank if text cannot be converted to a number.
Repeats a string a specified number of times.
**Syntax:** `REPT(text, number_of_times)`
**Business Example:**
```javascript theme={null}
// Repeat a character to match a rating or count
REPT('*', PRODUCTS."Rating")
// Create padding
REPT(' ', 10)
// Create separators
REPT('-', 20)
```
**Arguments:**
* `text`: String to repeat
* `number_of_times`: Number of repetitions
Use for padding, separators, or repeating a character a fixed number of times.
Extracts text using a regular expression pattern.
**Syntax:** `REGEXEXTRACT(text, pattern)`
**Escape backslashes in regex patterns.** Calculation strings parse `\` as an escape character, so any regex metacharacter that uses a backslash must be written with a doubled backslash. Use `'\\d'` (not `'\d'`), `'\\s+'`, `'\\.'`, `'\\(\\d{3}\\)'`, etc. A single backslash will be stripped before the regex engine sees the pattern, causing the match to silently fail and return an empty value. For literal characters that don't strictly need escaping in regex (such as a pipe), prefer a character class — `'[|]'` — to sidestep escaping entirely. This applies to `REGEXEXTRACT`, `REGEXMATCH`, and `REGEXREPLACE`.
**Business Example:**
```javascript theme={null}
// Extract phone area code
REGEXEXTRACT(CUSTOMERS."Phone", '\\((\\d{3})\\)')
// Extract email domain
REGEXEXTRACT(CUSTOMERS."Email", '@(.+)')
// Extract order number
REGEXEXTRACT(ORDERS."Reference", 'ORD-(\\d+)')
```
**Arguments:**
* `text`: Text to extract from
* `pattern`: Regular expression pattern
Requires knowledge of regular expressions. Use with caution.
**Returns the first match only**, not a list of matches. There is no built-in way to extract every match in one call.
Patterns follow **Java-style regex syntax**. Most expressions port unchanged from JavaScript, Python, or PCRE, but watch for escape handling and a few advanced constructs. See [Calculations troubleshooting](/data/calculations-troubleshooting#regex-syntax-java-style) for the differences that matter.
Tests if text matches a regular expression pattern.
**Syntax:** `REGEXMATCH(text, pattern)`
**Business Example:**
```javascript theme={null}
// Validate email format
REGEXMATCH(CUSTOMERS."Email", '^[\\w\\.-]+@[\\w\\.-]+\\.[a-zA-Z]{2,}$')
// Check phone format
REGEXMATCH(CUSTOMERS."Phone", '^\\(\\d{3}\\) \\d{3}-\\d{4}$')
// Validate product code
REGEXMATCH(PRODUCTS."SKU", '^[A-Z]{3}-\\d{4}$')
```
**Arguments:**
* `text`: Text to test
* `pattern`: Regular expression pattern
Returns TRUE if pattern matches, FALSE otherwise.
**`REGEXMATCH` does not enforce input format.** It only returns a boolean — it does **not** block saving a record, display a validation error to the user, or revert a bad value on its own. To act on a non-match, use the result in an automation (for example, block a stage transition, send a notification, or set a status flag when `REGEXMATCH` returns `FALSE`). To require a format on the form itself, use the **Required** flag and **Helper Text** in the [Form Builder](/workflows/form-builder) — there is no built-in regex mask on text fields.
Patterns follow **Java-style regex syntax**. See [Calculations troubleshooting](/data/calculations-troubleshooting#regex-syntax-java-style) for syntax differences if you're porting patterns from another flavor.
Replaces text using regular expression patterns.
**Syntax:** `REGEXREPLACE(text, pattern, replacement, [case_insensitive])`
**Business Example:**
```javascript theme={null}
// Format phone numbers
REGEXREPLACE(CUSTOMERS."Phone", '(\\d{3})(\\d{3})(\\d{4})', '($1) $2-$3')
// Clean product codes
REGEXREPLACE(PRODUCTS."SKU", '[^A-Z0-9-]', '')
// Standardize names
REGEXREPLACE(CUSTOMERS."Name", '\\s+', ' ')
```
**Arguments:**
* `text`: Text to modify
* `pattern`: Regular expression pattern
* `replacement`: Replacement text
* `case_insensitive`: \[OPTIONAL] TRUE for case-insensitive matching
Advanced feature requiring regex knowledge.
Replaces **all** matches of the pattern in the string, not just the first.
Patterns follow **Java-style regex syntax**. Backreferences in the replacement string use `$1`, `$2`, etc. See [Calculations troubleshooting](/data/calculations-troubleshooting#regex-syntax-java-style) for syntax differences if you're porting patterns from another flavor.
Escapes special characters in a string for use in JSON.
**Syntax:** `JSON_ESCAPE(text)`
**Business Example:**
```javascript theme={null}
// Escape a string with quotes and newlines
JSON_ESCAPE('Hello "world" with\nnewlines')
// Result: "Hello \\"world\\" with\\\\nnewlines"
```
**Arguments:**
* `text`: Text to escape
This function is useful for safely embedding text into JSON payloads.
Learn how to send JSON data to external systems.
Learn how to parse and import data from JSON files.
Unescapes special characters in a JSON string.
**Syntax:** `JSON_UNESCAPE(text)`
**Business Example:**
```javascript theme={null}
// Unescape a JSON-escaped string
JSON_UNESCAPE('Hello \\"world\\" with\\\\nnewlines')
// Result: "Hello \"world\" with\nnewlines"
```
**Arguments:**
* `text`: Text to unescape
This is the inverse of JSON\_ESCAPE, useful for parsing data from JSON payloads.
Learn how to send JSON data to external systems.
Learn how to parse and import data from JSON files.
### Mathematical Functions
Raises a number to a specified power.
**Syntax:** `POWER(base, exponent)`
**Business Example:**
```javascript theme={null}
// Calculate compound interest
POWER(1.05, YEARS."Investment")
// Calculate area of square
POWER(DIMENSIONS."Side", 2)
// Calculate exponential growth
POWER(GROWTH."Rate", PERIODS."Number")
```
**Arguments:**
* `base`: Base number
* `exponent`: Power to raise to
Any number raised to the power of 0 equals 1.
Calculates the square root of a number.
**Syntax:** `SQRT(number)`
**Business Example:**
```javascript theme={null}
// Calculate standard deviation component
SQRT(VARIANCE."Value")
// Calculate distance formula component
SQRT(COORDINATES."X" * COORDINATES."X" + COORDINATES."Y" * COORDINATES."Y")
// Calculate geometric mean component
SQRT(METRICS."Value1" * METRICS."Value2")
```
**Arguments:**
* `number`: Number to find square root of
Returns null if the number is negative.
### Special Functions
Returns the Boolean value TRUE.
**Syntax:** `TRUE()`
**Business Example:**
```javascript theme={null}
// Set default active status
TRUE()
// Use in conditional logic
IF(CUSTOMERS."Status" = 'Active', TRUE(), FALSE())
// Initialize flags
TRUE()
```
Useful for setting boolean field values and conditional logic.
Returns the Boolean value FALSE.
**Syntax:** `FALSE()`
**Business Example:**
```javascript theme={null}
// Set default inactive status
FALSE()
// Use in conditional logic
IF(ORDERS."Amount" > 0, TRUE(), FALSE())
// Initialize flags
FALSE()
```
Useful for setting boolean field values and conditional logic.
Returns a blank/null value.
**Syntax:** `BLANK()`
**Business Example:**
```javascript theme={null}
// Clear a field conditionally
IF(ORDERS."Status" = 'Cancelled', BLANK(), ORDERS."ShipDate")
// Set default empty value
BLANK()
// Use in conditional assignments
IF(CUSTOMERS."Type" = 'Guest', BLANK(), CUSTOMERS."LoyaltyPoints")
```
Represents the absence of data, different from empty string.
Tests if a value is blank/null.
**Syntax:** `ISBLANK(value)`
**Business Example:**
```javascript theme={null}
// Check if customer has phone number
ISBLANK(CUSTOMERS."Phone")
// Validate required fields
ISBLANK(ORDERS."ShippingAddress")
// Check for missing data
ISBLANK(PRODUCTS."Description")
// Conditional logic based on blank values
IF(ISBLANK(CUSTOMERS."Email"), 'No Email', 'Has Email')
```
**Arguments:**
* `value`: Value to test for blankness
Returns TRUE if the value represents an absence of data — an empty text value (`''`) or a true null both count as blank. Returns FALSE if the value contains any actual data, including a text field whose content is the four-character string `null` (an actual text value that's different from a true absence of value) — see [Calculations troubleshooting](/data/calculations-troubleshooting#detecting-missing-values-including-the-literal-text-null) for the pattern to detect that case.
Generates a random UUID (Universally Unique Identifier).
**Syntax:** `UUID()`
**Business Example:**
```javascript theme={null}
// Generate unique transaction ID
UUID()
// Create unique reference number
UUID()
// Generate API key
UUID()
```
Returns a string in format: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6'
## Troubleshooting
**Problem:** Your calculation returns blank instead of expected values.
**Causes & Solutions:**
* **Blank input data**: Check that referenced fields contain data
* **Invalid field references**: Ensure field names are correct and properly quoted
* **Type mismatches**: Verify you're using the right function for your data type
**Example Fix:**
```javascript theme={null}
// Instead of this (might return blank):
AVERAGE(ORDERS."Amount")
// Try this (handles blanks better):
IF(COUNT(ORDERS."Amount") > 0, AVERAGE(ORDERS."Amount"), 0)
```
**Prevention:**
* Always test with sample data
* Use ISBLANK() to check for missing data
* Validate field names match exactly
**Problem:** Functions like SUM, COUNT, AVERAGE don't work with fields from the current record.
**Solution:** These functions only work with related fields. For current record calculations, use operators:
```javascript theme={null}
// Wrong - won't work:
SUM(CURRENT."Field1", CURRENT."Field2")
// Right - use operators:
CURRENT."Field1" + CURRENT."Field2"
// Right - for related data:
SUM(RELATED_RECORDS."Field")
```
**Key Point:** Aggregate functions (SUM, COUNT, AVERAGE, etc.) are designed for related data, not individual field operations.
**Problem:** Date calculations returning unexpected results.
**Common Fixes:**
* **Text dates**: Use `DATEVALUE()` to convert text to proper dates
* **Timezone issues**: Ensure consistent timezone handling
* **Format problems**: Check date format consistency
**Example Fix:**
```javascript theme={null}
// If date is stored as text:
DATEDIF(DATEVALUE(CUSTOMERS."SignupDate"), NOW(), 'D')
// For consistent date creation:
DATE(YEAR(NOW()), MONTH(NOW()), 1)
// Handle blank dates:
IF(ISBLANK(ORDERS."ShipDate"), 'Not Shipped', DATEDIF(ORDERS."OrderDate", ORDERS."ShipDate", 'D'))
```
**Problem:** FIND vs SEARCH, UPPER vs LOWER, concatenation issues.
**Solutions:**
* **FIND**: Case-sensitive search
* **SEARCH**: Case-insensitive search
* **CONCAT**: Joins multiple values
* **Always use single quotes** for text literals
**Example Fixes:**
```javascript theme={null}
// Case-sensitive search:
FIND('Manager', EMPLOYEES."Title")
// Case-insensitive search:
SEARCH('manager', EMPLOYEES."Title")
// Proper concatenation:
CONCAT(CUSTOMERS."FirstName", ' ', CUSTOMERS."LastName")
// Wrong - don't use double quotes:
CONCAT(CUSTOMERS."FirstName", " ", CUSTOMERS."LastName")
```
**Problem:** Field references not working, getting 'field not found' errors.
**Solutions:**
* **Check field names**: Must match exactly (case-sensitive)
* **Use proper syntax**: HANDLE."FieldName" format
* **Verify relationships**: Ensure fields are properly related
**Example Fixes:**
```javascript theme={null}
// Correct field reference:
CUSTOMERS."FirstName"
// Wrong - missing quotes:
CUSTOMERS.FirstName
// Wrong - incorrect case:
CUSTOMERS."firstname"
// For related fields:
RELATED_CUSTOMERS."FirstName"
```
**Problem:** Calculations running slowly or timing out.
**Solutions:**
* **Simplify complex calculations**: Break into smaller parts
* **Avoid nested functions**: Use intermediate calculations
* **Check data volumes**: Large datasets may need optimization
**Example Optimization:**
```javascript theme={null}
// Instead of nested complexity:
IF(AND(CUSTOMERS."Status" = 'Active', DATEDIF(CUSTOMERS."LastPurchase", NOW(), 'D') < 30), 'Recent', 'Old')
// Break it down:
// Step 1: Days since purchase
DATEDIF(CUSTOMERS."LastPurchase", NOW(), 'D')
// Step 2: Use result in simpler IF
IF(PREVIOUS_CALC < 30, 'Recent', 'Old')
```
## Best Practices
These guidelines help keep calculations reliable and easy to maintain.
* Use descriptive field names that clearly indicate purpose
* Keep calculations simple and readable
* Break complex logic into multiple steps
* Document complex calculations with comments
* Always check for blank values using ISBLANK()
* Validate data types before performing operations
* Use IF statements to handle edge cases
* Test calculations with various data scenarios
* Avoid deeply nested functions
* Use intermediate calculations for complex logic
* Consider data volume when designing calculations
* Test performance with realistic data sets
* Use proper field reference syntax: HANDLE."FieldName"
* Always use single quotes for text literals, never double quotes
* Verify field relationships before using aggregate functions
* Test calculations thoroughly before deployment
## Related documentation
* **[Core concepts](/getting-started/fundamentals/core-concepts)** — Apps, Elements, fields, and how records connect
* **[Tables](/data/tables)** — Calculated columns and spreadsheet-style views of your data
* **[Showing relationships](/data/showing-relationships)** — Related records and how aggregates apply to related fields
* **[Data best practices](/data/data-best-practices)** — Structuring data so formulas and reports stay maintainable
* **[Automation system](/workflows/automation-system)** — Triggers and actions where calculations often appear
## Need More Help?
Support options, self-help topics, and how to reach the team
Get direct help from our support team
Learn advanced calculation techniques
Watch step-by-step calculation examples
# Calculations troubleshooting
Source: https://docs.elementum.io/data/calculations-troubleshooting
Common calculation gotchas—multi-value fields, array shapes, regex syntax, date parsing—with the recommended pattern for each.
Calculations work well for single-value math, text, dates, and conditional logic against a single record or a related set of records. A handful of shapes and operations behave differently than people expect — especially folks coming from spreadsheets or SQL. This page collects the most common gotchas, the errors you're likely to see, and the recommended path forward for each.
If you're here because of a specific error, jump to the [Error reference](#error-reference).
This page reflects current behavior. Where a section notes that something isn't supported, it may change in a future release — the section will say so when that's the case.
## Multi-value and array-shaped values
The most common source of confusion. Two related shapes:
* **Multi-picklist fields** — fields whose type is `MULTI_PICKLIST`. A single cell holds several selected options.
* **Multi-value text columns surfaced through Data Mine** — when a [Data Mine](/data/data-mining) trigger fires, multi-value text columns in the payload serialize as a JSON array of objects, not as plain text. The shape looks like this:
```json theme={null}
[
{ "value": "WD", "type": "TEXT" },
{ "value": "ZIP", "type": "TEXT" }
]
```
Both shapes are *lists* of values, and almost every calculation function expects a *single* value at a time. The two limitations below cover what that means in practice.
If you're working with a multi-picklist field and want the working patterns for iteration, aggregation, and passing selections into an API body, see [Multi-value fields](/data/multi-value-fields). This section covers what specifically breaks inside a calculation.
### Aggregating a multi-picklist into a delimited string
**Not currently supported.** Aggregations over list-typed fields aren't supported in calculations.
**What people try:**
```javascript theme={null}
STRING_AGG_UNIQUE(CLAI."Claim Edit(s)", ',')
```
**What they get:**
```
Invalid Type Error at line 1, position 18: MULTI_PICKLIST
```
`STRING_AGG` and `STRING_AGG_UNIQUE` only accept single-value text supplied via a related-field aggregation — they cannot operate on a `MULTI_PICKLIST` field directly. There is no calculation function that aggregates the selections of a multi-picklist into a delimited string.
**What works instead.** Do the aggregation in an [Execute Script](/workflows/automation-actions-reference#execute-script) action and, if the result needs to live on a record, write it back with **Update Record Fields**. The full pattern (input mapping, iteration, aggregation, and passing the result into a Send API Request body) is on [Multi-value fields](/data/multi-value-fields).
### Flattening a Data Mine array payload into plain text
**Not currently supported.** Calculations cannot convert structures — the `[{"value":..., "type":...}, ...]` shape that Data Mine produces for multi-value text columns cannot be flattened into plain delimited text inside a calculated column or the field mapping on a Create Record action.
**What happens if you try:** the destination text field stores the literal JSON, for example:
```
[{"value":"WD","type":"TEXT"},{"value":"ZIP","type":"TEXT"}]
```
…rather than the friendly `WD, ZIP` users expect.
## SPLIT returns an array you can't index into
`SPLIT(text, delimiter)` returns an array of substrings. That's expected — but **selecting a single element from that array isn't supported in calculations.** There's no array indexing syntax (`[0]`, `[1]`, etc.) and no `INDEX`/`NTH` function to pick a piece.
**Example of the trap:**
```javascript theme={null}
// Returns the array ['TPFN', '123'] — but you can't extract 'TPFN' from it.
SPLIT('TPFN-123', '-')
```
**Recommended patterns:**
* If the position is **genuinely fixed** (for example, every ID is always exactly 4 characters), use `LEFT`, `RIGHT`, or `MID`. These return text you can use directly.
* If the split point is variable (for example, "everything before the dash"), combine `FIND` or `SEARCH` to locate the delimiter with `LEFT`/`MID` to extract the piece you want:
```javascript theme={null}
// 'Everything before the first dash' in CONTRACTS."Reference"
LEFT(CONTRACTS."Reference", FIND('-', CONTRACTS."Reference") - 1)
```
The `LEFT`/`RIGHT`/`MID` pattern only stays correct while the position is truly fixed. If the value's length can change — for example, IDs growing from `99` to `100` add a character — the calculation will silently start returning the wrong substring without raising an error. For anything length-variable, use the `FIND`/`SEARCH` + `LEFT`/`MID` pattern instead.
## DATEVALUE only accepts year-month-day input
`DATEVALUE(text_date)` doesn't auto-detect or parse arbitrary date formats. The input must be in `YYYY-MM-DD` form — **date only, no time component.** Other formats won't parse and the function returns blank, including:
* US-style `MM/DD/YYYY` or European `DD/MM/YYYY`
* Written-out months like `Jun 1, 2026`
* ISO 8601 strings with a time component like `2026-06-01 12:34:56` or `2026-06-01T12:34:56Z`
**Recommended patterns:**
* Normalize date text to `YYYY-MM-DD` in the source system or an ingest step before it reaches the calculation.
* If the components are available as separate values, skip `DATEVALUE` entirely and build the date with [`DATE(year, month, day)`](/data/calculations#date-and-time-functions).
* For text dates with predictable structure, use `LEFT`/`MID`/`RIGHT` to rearrange the pieces into year-month-day before passing to `DATEVALUE`.
## Regex syntax: Java-style
`REGEXEXTRACT`, `REGEXMATCH`, and `REGEXREPLACE` are fully supported. Standard regex operations — character classes, quantifiers, groups, anchors, alternation, backreferences — work as expected.
**`REGEXEXTRACT` returns the first match only** — not a list of matches. There is no built-in way to extract every match in one call.
**`REGEXREPLACE` replaces all matches** of the pattern in the string, not just the first.
Patterns follow **Java-style regex syntax** because of the backend implementation. If you're porting patterns from JavaScript, Python, or PCRE-flavored tools, most expressions work unchanged, but watch for the differences that bite:
* **Escaping inside string literals** — calculation text is enclosed in single quotes, so backslashes in patterns need to be escaped: write `'\\d+'` (not `'\d+'`) to match one or more digits.
* **A few advanced constructs** — possessive quantifiers (`*+`, `++`) and certain Unicode property classes behave per the Java spec, not PCRE.
The [Java `Pattern` javadoc](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html) is the authoritative syntax reference. When in doubt, test the pattern with a small string against `REGEXMATCH` before building a larger calculation around it.
## Adding or subtracting days from a date
Use [`DATEADD(unit, value, date)`](/data/calculations#dateadd---add-or-subtract-time-from-a-date). Unit tokens are unquoted, and a negative value subtracts:
```javascript theme={null}
// Today + 14 days
DATEADD(DAY, 14, NOW())
// A due date minus 3 days
DATEADD(DAY, -3, TASKS."DueDate")
// One month before a due date
DATEADD(MONTH, -1, TASKS."DueDate")
```
For "is this date yesterday/today/tomorrow" comparisons, use [`DATEDIF`](/data/calculations#date-and-time-functions) against `NOW()` with the `'D'` unit:
```javascript theme={null}
// TRUE when the field's date is exactly yesterday (FALSE for today)
DATEDIF(YOUR_ELEMENT."DateField", NOW(), 'D') = 1
```
## IF branches must return the same type
Both branches of `IF(condition, value_if_true, value_if_false)` must return the same data type. Mixing types raises a validation error and the calculation won't save.
A common trip-up is using `''` (empty string) as a placeholder in the false branch of an otherwise date-typed expression:
```javascript theme={null}
// Incorrect — the true branch returns a date, the false branch returns text
IF(ISBLANK(ACQ."Close Date"), '', ACQ."Close Date" + 30)
```
Use [`BLANK()`](/data/calculations#blank---return-blank-value) instead. It represents the absence of a value without changing the branch's type, so both sides stay date-typed:
```javascript theme={null}
// Correct — both branches are date-typed
IF(ISBLANK(ACQ."Close Date"), BLANK(), ACQ."Close Date" + 30)
```
The same rule applies for numeric and text branches — use `BLANK()` (not `0` or `''`) when one branch has no meaningful value.
## Detecting missing values, including the literal text 'null'
`ISBLANK(field)` detects when a field has no value at all. Elementum normalizes empty text and true nulls the same way at the field level — `ISBLANK` returns TRUE for both. In practice you don't need a separate check for "null vs. blank" on a field.
What `ISBLANK` does **not** catch is a text field whose *content* is the four characters `n`, `u`, `l`, `l`. This shows up when the field was populated by an upstream system that writes the string `null` as a marker for missing data — API responses, webhook payloads, and some CSV exports do this. From the calculation's perspective the field holds a non-empty text value, so `ISBLANK` returns FALSE.
To detect that literal string, compare against the quoted text:
```javascript theme={null}
// TRUE when the field's text content is the four characters n-u-l-l
SOURCE."StatusAsOfDate" = 'null'
```
To cover both shapes in one condition, combine the checks with `OR`:
```javascript theme={null}
// TRUE when the field is empty OR contains the text 'null'
OR(ISBLANK(SOURCE."StatusAsOfDate"), SOURCE."StatusAsOfDate" = 'null')
```
`null` is not a keyword in the calculation language. Writing `null` bare (unquoted) raises a syntax error like `Invalid Syntax Error at line 1, position N: missing ')' at ','`. Use [`BLANK()`](/data/calculations#blank---return-blank-value) to *return* an empty value from a branch of an `IF`, and `'null'` (quoted) to compare against the literal text.
### Worked example: text-to-date, empty when the source is 'null'
Convert a text field to a date, and return blank — not today's date — when the source is empty or contains the string `null`:
```javascript theme={null}
IF(
OR(ISBLANK(SOURCE."StatusAsOfDate"), SOURCE."StatusAsOfDate" = 'null'),
BLANK(),
DATEVALUE(SOURCE."StatusAsOfDate")
)
```
A few details worth calling out:
* **Both branches must be date-typed.** `BLANK()` on the true branch takes the surrounding branch's type — `''` (empty string) is text-typed and would raise a validation error. See [IF branches must return the same type](#if-branches-must-return-the-same-type).
* **`DATEVALUE` only parses `YYYY-MM-DD`.** If the source date is in another format, `DATEVALUE` returns blank silently rather than the value you expect — see [DATEVALUE only accepts year-month-day input](#datevalue-only-accepts-year-month-day-input).
* **`IF` takes exactly three arguments** — condition, value if true, value if false. Nested checks belong inside the condition (as with `OR` above), not as extra positional arguments in the outer `IF(...)`.
## Inserting text into the middle of a string
There is no dedicated "insert" function. Split the string at the target position with `LEFT` and `RIGHT`, then reassemble it with [`CONCAT`](/data/calculations#concat---join-text-together):
```javascript theme={null}
// Insert " Sorry" after the first 11 characters of "Hello world this is ME"
CONCAT(
LEFT(SOURCE."Text", 11),
' Sorry',
RIGHT(SOURCE."Text", LEN(SOURCE."Text") - 11)
)
// Result: "Hello world Sorry this is ME"
```
When the insertion point isn't at a fixed offset, use [`FIND`](/data/calculations#find---find-text-position-case-sensitive) to locate the anchor dynamically:
```javascript theme={null}
// Insert " Sorry" immediately after the word "world"
CONCAT(
LEFT(SOURCE."Text", FIND('world', SOURCE."Text") + 4),
' Sorry',
RIGHT(SOURCE."Text", LEN(SOURCE."Text") - (FIND('world', SOURCE."Text") + 4))
)
```
`SUBSTITUTE(text, old_text, new_text)` is a different tool — it *replaces* every match of `old_text`. It only produces an "insert" when you replace an anchor with itself plus the new text (for example, `SUBSTITUTE(text, 'world', 'world Sorry')`), which requires a unique anchor and rewrites every occurrence.
`FIND` is case-sensitive and returns the position of the first match. If the anchor could appear more than once in the source text, this pattern inserts before the first occurrence only.
### Worked example: building an HTML email body from records
A common driver for this pattern is assembling an HTML email body from a list of records — the outer template is fixed, and you need to drop `` rows into the `
` before sending. The template looks like this:
```html theme={null}
```
Given a `template` value holding that markup and a `new_rows` value holding the row markup to insert — for example:
```html theme={null}
| Jane Doe | Paid |
```
split the template around `` and stitch the rows in front of it:
```javascript theme={null}
CONCAT(
LEFT(template, FIND('', template) - 1),
new_rows,
RIGHT(template, LEN(template) - FIND('', template) + 1)
)
```
What each piece does:
* `FIND('', template)` locates the character position where the closing tag starts.
* `LEFT(template, FIND(...) - 1)` returns everything in the template *before* the closing tag.
* `RIGHT(template, LEN(template) - FIND(...) + 1)` returns the closing tag and everything after it, so the tag itself is preserved.
* `CONCAT(...)` glues the three pieces back together in order — before, rows, after.
**Build multi-row markup as its own calculation first.** When rows come from several related records (one row per line item, for example), aggregate the row markup in its own calculation and pass the result into the insert formula as `new_rows`. Nesting the aggregation and the insertion in a single expression is hard to read and hard to debug.
### The same pattern in an Execute Script action
If your automation already uses an [Execute Script](/workflows/automation-actions-reference#data-actions) action for other logic, JavaScript's `String.prototype.replace` is shorter and avoids off-by-one arithmetic with character positions. Replace the marker with the new content followed by the marker itself:
```javascript theme={null}
const { template, newRows } = input.parameters;
const result = template.replace('', `${newRows}`);
return { emailBody: result };
```
When rows come from a list of records, build the row markup with `.map(...).join('')` and insert once:
```javascript theme={null}
const { template, records } = input.parameters;
const rowsHtml = records
.map(r => `| ${r.name} | ${r.status} |
`)
.join('');
const result = template.replace('', `${rowsHtml}`);
return { emailBody: result };
```
Two failure modes worth checking if the script raises `Cannot read property 'replace' of undefined`:
* `records` must be the array of record data, not a single record or an unresolved reference. Access it via `input.parameters` — parameter names are not injected as standalone variables in the script scope (see [Execute Script](/workflows/automation-actions-reference#data-actions)).
* `template` must be a string at the point you call `.replace()` on it. A field reference that hasn't loaded yet resolves to `undefined`.
For more than one insertion point in the same template (for example, rows inside `` **and** a summary line before `