Expedition. Free, virtual, Nov 3–6.

Technical tracks for practitioners, outcomes for leaders.

Snowflake for Developers/Guides/Build Full-Stack Apps with Snowflake App Runtime
Quickstart

Build Full-Stack Apps with Snowflake App Runtime

Build
Kamesh Sampath

Overview

Three prompts. Three apps. Zero infrastructure.

You will build ChurnGuard, a customer retention app, by describing what you want to Cortex Code Desktop. Each iteration adds one prompt and one kind of app:

IterationWhat You BuildSnowflake Features
1 — Explore your dataA churn dashboard you can pivot and drill into. See the risk. Explore the why.Application Service, querySnowflake, app.yml
2 — Run the workflowAn Action Board where teams flag customers, assign actions, and track them to resolution. Turn insight into action.Snowflake Postgres, data mirroring, dynamic tables, alerts, External Access Integration
3 — Bring AI into the appA Cortex Agent inside the app that answers questions and proposes actions for you to approve. Ask your data. Get a next step.Cortex Agent, semantic view, serverless task, MCP server

Snowflake App Runtime runs the app on Snowflake as an APPLICATION SERVICE. You don't build Docker images, manage a registry, or set up a pipeline. One command deploys it.

Each iteration ends with a working app. Session leaders can stop after any of them.

Prompt Approach

Each iteration uses an Intent-Driven Development (IDD) prompt. It tells Cortex Code what you want, not how to build it:

SectionPurpose
GoalThe outcome you want
RequirementsWhat the app must do
UILook, layout, and interactions
ConstraintsScope and safety rules
OutputWhat success looks like

What You'll Learn

The three iterations map to the three kinds of app Snowflake App Runtime is built for:

  • Advanced data exploration: build an interactive app on governed data, with full control of the UI, that queries Snowflake without managing credentials
  • Workflow apps: read and write governed data with shared state and approvals. Keep operational data (fast row inserts and updates) in Snowflake Postgres, mirror it to Snowflake, and let dynamic tables and alerts carry the logic so the app stays thin
  • AI apps and agents: embed a Cortex Agent that reasons over your data and calls your app's tools, and expose those tools as an MCP server for agentic automation
  • The App Runtime lifecycle: the describe, build, deploy, iterate loop, the app.yml manifest, and in-place upgrades at a stable URL

What You'll Need

Important: Snowflake App Runtime isn't available on trial accounts. You need an account where App Runtime is enabled.

  • A Snowflake account with Snowflake App Runtime enabled.

  • ACCOUNTADMIN access for the setup scripts, which:

    • Create a custom role and database
    • Grant BIND SERVICE ENDPOINT so the app gets a URL
    • Grant CREATE EXTERNAL ACCESS INTEGRATION and CREATE NETWORK POLICY for Postgres connectivity (iteration 2)
    • Grant the snowflake.postgres_mirror_admin application role for data mirroring (iteration 2)
    • Grant the SNOWFLAKE.CORTEX_USER database role for the Cortex Agent (iteration 3)

    After setup, you work as the dedicated SFQUICKSTART_CHURNGUARD_ROLE.

You probably have Git and Node.js already. The table lists every tool so you can check versions. psql and the Cortex Code CLI are optional:

ToolVersionUsed forGet it
Cortex Code DesktopLatestRunning the prompts that build the appDownload
Cortex Code CLILatestOptional: calling the app's MCP server (iteration 3)Install
Snowflake CLI3.26.0+Running scripts and deploying the appInstall
Node.js and npm20+Building the app locallyDownload
GitAny recentCloning the repo and switching iterationsDownload
psqlAny recentOptional: connecting to Postgres from your terminal (iteration 2)Download or brew install libpq on macOS

If you want to try the MCP server in iteration 3, install the Cortex Code CLI:

# macOS, Linux, or WSL
curl -LsS https://ai.snowflake.com/static/cc-scripts/install.sh | sh

# Windows (PowerShell)
irm https://ai.snowflake.com/static/cc-scripts/install.ps1 | iex

Environment Setup

Verify Tools

Check the required tools. Snowflake CLI must be 3.26.0 or later and Node.js 20 or later:

# Check the required and optional tools
snow --version
node --version
git --version

# optional tools
psql --version
cortex --version

Clone the Repository

The repo holds the setup scripts and the prompt for each iteration:

# Clone the companion repo
git clone https://github.com/Snowflake-Labs/sfguide-build-full-stack-apps-with-snowflake-app-runtime.git
cd sfguide-build-full-stack-apps-with-snowflake-app-runtime

Tip: The solution branch has the finished ChurnGuard app through iteration 3. Use it to compare your app or to skip ahead, but don't merge it into my-app. Once you have my-app (below), compare at any point with:

git diff my-app origin/solution -- churnguard/

Add a Snowflake CLI Connection

Add a quickstart connection and test it:

# Add the quickstart connection, then test it
snow connection add --connection-name quickstart \
  --account <your_account> --user <your_user> \
  --warehouse SFQUICKSTART_CHURNGUARD_WH \
  --authenticator 'OAUTH_AUTHORIZATION_CODE' \
  --client-store-temporary-credential \
  --no-interactive

snow connection test --connection quickstart

The command writes the connection to your Snowflake config file. Replace your_account and your_user with your values. Cortex Code Desktop and the Cortex Code CLI read the same file.

Note: The guide uses the connection name quickstart. If you use another name, change it in .env, AGENTS.md, and every --connection flag (including cortex mcp add in iteration 3).

Run the Setup Scripts

Run the three setup scripts in order:

# Run the setup scripts in order
snow sql -f scripts/setup.sql --connection quickstart       # database and TPC-DS check
snow sql -f scripts/grants.sql --connection quickstart      # quickstart role and grants
snow sql -f scripts/setup-data.sql --connection quickstart  # sample data (usually under a minute)

What each one does:

  • setup.sql creates the SFQUICKSTART_CHURNGUARD database and the SFQUICKSTART_CHURNGUARD_WH warehouse (X-Small, suspends after 60 seconds idle), and checks for the TPC-DS sample data (about 65 million rows). If your account doesn't have it, the script creates it from the Snowflake share. You can also get it from Snowflake Marketplace.
  • grants.sql creates SFQUICKSTART_CHURNGUARD_ROLE and grants it to you. The role owns the quickstart database and can deploy apps, use the warehouse, and read the sample data. Each iteration adds its own grants when you rerun the script (Postgres in iteration 2, Cortex AI in iteration 3).
  • setup-data.sql samples the TPC-DS 10 TB dataset once into two small tables, CHURN_METRICS (about 30,000 customers) and CHURN_TRENDS (monthly totals), so dashboard queries return in under a second. It samples with TABLESAMPLE SYSTEM instead of scanning the 29-billion-row fact tables.

Check the tables:

snow sql --connection quickstart --role SFQUICKSTART_CHURNGUARD_ROLE -q "
  SELECT 'CHURN_METRICS' AS tbl, COUNT(*) AS row_count
    FROM SFQUICKSTART_CHURNGUARD.PUBLIC.CHURN_METRICS
  UNION ALL
  SELECT 'CHURN_TRENDS', COUNT(*)
    FROM SFQUICKSTART_CHURNGUARD.PUBLIC.CHURN_TRENDS"

Expect about 30,000 rows in CHURN_METRICS and 12 rows (one per month) in CHURN_TRENDS. TPC-DS is a benchmark dataset, so its dates come from the early 2000s rather than this year.

Prepare Your Working Directory

Create your own working branch, my-app, from the iteration 1 branch:

# Create my-app from the iteration 1 branch
git fetch origin
git checkout -b my-app origin/iteration-1/data-exploration

The branch includes AGENTS.md, which tells Cortex Code your connection, role, database, and conventions. Cortex Code reads it automatically.

Set the default connection and role. The branch also includes .env.example, which makes quickstart your default connection and SFQUICKSTART_CHURNGUARD_ROLE its role. Load it now that grants.sql has created the role:

# Load the default connection and role
cp .env.example .env

# with direnv (https://direnv.net)
echo dotenv > .envrc && direnv allow
# or, without direnv, run this instead in each new terminal:
# set -a; source .env; set +a

snow connection test   # should show quickstart with SFQUICKSTART_CHURNGUARD_ROLE

Set the connection's default role and warehouse. snow app deploy uses the connection's default role, not --role or .env, so set it in your Snowflake config file (~/.snowflake/connections.toml). Add these two lines to the [quickstart] block, or update them if they're already there:

role = "SFQUICKSTART_CHURNGUARD_ROLE"
warehouse = "SFQUICKSTART_CHURNGUARD_WH"

The setup scripts still switch to ACCOUNTADMIN themselves, so you can rerun grants.sql later without changing anything.

Snowflake CLI commands, the Cortex Code CLI, and npm run dev started from this terminal now use that connection and role. The files hold no secrets, and the repo's .gitignore keeps .env and .envrc out of your commits.

Open the repo directory in Cortex Code Desktop. Your account name should appear in the status bar.

Cortex Code Desktop connected to Snowflake

How your app carries over. Cortex Code generates the app in churnguard/ on your my-app branch. Each iteration branch adds only that iteration's prompts and scripts, so at the start of the next iteration you commit your app and merge the next branch in. Edit only files in churnguard/ and every merge stays conflict-free.

Iteration 1: Build the Data Explorer

See the risk. Explore the why.

A static report shows you numbers. An explorer lets you ask why. In this iteration one prompt builds a churn dashboard you can pivot and drill into, and deploys it.

The Prompt

Open the prompt file 01-build-explorer.md and paste it into Cortex Code Desktop. Here it is in full, so you can see how an IDD prompt is put together:

/snowflake-apps

Goal: Build a customer churn risk dashboard called ChurnGuard
that helps retention teams identify and prioritize at-risk customers.

Requirements:
- KPI cards: total customers, churn rate, revenue at risk, avg return rate
- Segment chart: churn risk distribution by credit rating (High/Medium/Low)
- Trend chart: active customers vs churn indicator over time
- Customer details table showing individual high-risk customers
  with 10-per-page pagination and risk level filter tabs
- Drill down: clicking a segment in the churn-risk chart filters the
  customer table to that segment; a "Clear filter" button resets it
- Pivot: a dimension selector above the segment chart lets users switch
  the grouping column (credit rating, marital status, education level,
  or gender) and the chart re-aggregates on selection
- Query the pre-computed tables in SFQUICKSTART_CHURNGUARD.PUBLIC:
  CHURN_METRICS (customer-level data) and CHURN_TRENDS (monthly aggregates)

UI:
- Modern card-based layout with generous spacing and subtle shadows
- Professional color palette (blues and grays with accent colors for risk levels)
- Responsive grid that works well on wide screens
- Smooth loading states and transitions
- Data-dense but not cluttered -- prioritize readability

Constraints:
- Query only the local CHURN_METRICS and CHURN_TRENDS tables
  (do NOT query SNOWFLAKE_SAMPLE_DATA directly -- the fact tables are too large)
- Add the usage markers from AGENTS.md (APP_ORIGIN in app.yml,
  QUERY_TAG on the connection with "iter":"1")

Output:
- Deployed interactive dashboard at the Application Service URL
- Clean Next.js project structure ready for iteration

How the prompt is built:

  • /snowflake-apps loads the skill that knows how to build App Runtime apps
  • Goal is the outcome, in one sentence
  • Requirements say what the app must do
  • UI describes the look and feel, not components
  • Constraints set scope and safety rules
  • Output says how you know it worked

The prompt never names a framework, a chart library, or a file. Those choices are left to Cortex Code. The later prompts follow the same structure.

What Happens

The build and deploy take a few minutes, so follow along in the chat. Cortex Code generates code with an LLM, so layout, chart styles, and colors vary between runs; your app will do the same things as the screenshots but may not look identical.

AGENTS.md tells Cortex Code where to deploy, so it works through the whole lifecycle:

1. Scaffold. Cortex Code copies the Next.js App Runtime starter and runs npm install.

2. Manifest and code. It runs snow app setup to generate the app.yml manifest, then writes:

  • API routes that query CHURN_METRICS and CHURN_TRENDS
  • A React frontend with KPI cards, a segment chart with drill-down, a trend chart, a pivot selector, and a customer table with risk badges
  • Styling that follows the prompt's UI section

3. Deploy. Cortex Code runs snow app deploy, watches the build, and gives you the App URL once the service is RUNNING.

Key Concepts

  • Owner's rights means the app's queries run as the service's own role, so the code holds no credentials and you don't grant anything per user. See Query Snowflake.

Verify

Tip: Run snow app open --connection quickstart to open the app in your browser, or add --print-only to print its URL. Add --watch if the deploy is still finishing.

Open the App URL. Click a segment in the churn-risk chart to filter the customer table, then switch the pivot to another dimension.

ChurnGuard Explorer dashboard

Note: The tables come from sampled TPC-DS data, so your numbers may differ if you rerun setup-data.sql.

The URL stays the same when you redeploy. The service upgrades in place, with no downtime.

Note: SHOW APPLICATION SERVICES may report SUSPENDED right after a deploy. That's normal: the service suspends when idle and resumes on the first request, so just open the URL.

Checkpoint: the dashboard filters by segment and pivots by dimension. You're ready for iteration 2.

Test Locally (Optional)

Start the dev server from churnguard/, then open http://localhost:3000. It connects to Snowflake with your CLI credentials.

npm run dev

Understanding app.yml

Open app.yml in your project root. snow app setup (Snowflake CLI 3.26.0+) generates it, and every App Runtime app uses it as its manifest. Cortex Code runs snow app setup for you; run it yourself only when you bring existing app code.

Tip: Have a project that uses snowflake.yml? See Migrate from snowflake.yml to app.yml.

app.yml in the editor

For every field, see the app.yml reference.

Key Fields

FieldPurpose
version: 2Required. Without it the CLI ignores the deployment keys
nameName of the APPLICATION SERVICE object
database / schemaWhere the app object lives. Both must exist
query_warehouseWarehouse for the app's SQL queries
label / description / iconHow the app is presented
ignoreFiles left out of the upload
auto_resumeResume the service when a request arrives (default true)
auto_suspend_secsIdle seconds before the service suspends (default 0, never; minimum 300)
environment_variablesPlain (non-secret) settings for the app. ChurnGuard sets APP_ORIGIN here (see the note below)

Each snow app deploy applies the whole manifest. Iteration 2 adds secrets and external_access_integrations so the app can reach Snowflake Postgres. Otherwise only the app code changes.

Note: ChurnGuard carries two anonymous usage markers: APP_ORIGIN in app.yml and a QUERY_TAG on every query the app runs. They tell Snowflake DevRel that an app started from this guide. They hold no customer data, and the app works the same without them. To opt out, delete both. AGENTS.md in the companion repo describes them.

Tip: With the default auto_suspend_secs of 0, the app never suspends and keeps consuming credits while idle. For a lab, set it to 300 (five minutes), or run the cleanup when you finish. See Scale and suspend apps.

Iteration 2: Add Workflow Capabilities

Turn insight into action.

A dashboard tells you who is at risk. It doesn't help you do anything about it. In this iteration ChurnGuard becomes a workflow app: teams flag customers, assign retention actions, and track them to resolution.

Iteration 2 architecture

The app reads and writes actions only in Snowflake Postgres, so the Action Board always shows the latest state. In Snowflake, data mirroring, a dynamic table, and an alert run separately. The only thing the app reads from that pipeline is the NOTIFICATIONS table.

Setup

Commit your iteration 1 work if you haven't already, then merge in iteration 2:

# Commit iteration 1, then merge in iteration 2
git add -A && git commit -m "Iteration 1 done"
git fetch origin && git merge --no-edit origin/iteration-2/workflow-app

Step 1 — Rerun grants. This adds the Postgres, External Access Integration, mirroring, and alert privileges:

snow sql -f scripts/grants.sql --connection quickstart

Step 2 — Create the Postgres instance and data mirror. Find your public IP address first (for example at ifconfig.me); the prompt asks for it so psql can create the table. Then open the prompt file 02-postgres-setup.md and paste it into Cortex Code Desktop. It uses the /snowflake-postgres skill to:

  • Create the CHURNGUARD_PG Postgres instance
  • Store credentials as the CHURNGUARD_PG_SECRET and CHURNGUARD_PG_HOST_SECRET secrets, right away, because the password is shown only once
  • Attach the CHURNGUARD_PG_POLICY network policy with two rules: CHURNGUARD_PG_INGRESS_RULE for Snowpark Container Services egress IPs, and a temporary CHURNGUARD_PG_LOCAL_IP_RULE for your IP
  • Create the actions table, with the primary key mirroring needs and an index that allows one open action per customer
  • Create a network rule and an External Access Integration for the app
  • Mirror the data to the CHURNGUARD_MIRROR database
  • Remove your IP from the policy, so only the app can reach Postgres

Note: The Postgres instance takes 3–5 minutes to provision. The prompt waits and checks for you. To use psql again later, see Troubleshooting.

Step 3 — Create the dynamic table, notifications table, and alert:

snow sql -f scripts/iteration-2-setup.sql --connection quickstart

This creates the ACTION_SUMMARY dynamic table, which reads the mirrored $live view, plus the NOTIFICATIONS table and ESCALATION_ALERT.

Note: From here on, some objects use credits while they run: the Postgres instance, the dynamic table and alert (both every minute), and, in iteration 3, a daily task. Run Cleanup when you're done.

The Prompt

Open the prompt file 02-add-workflow.md and paste it into Cortex Code Desktop. It asks for an Action Board, writes to Postgres with parameterized queries, and toast notifications driven by a Snowflake alert.

What Happens

Cortex Code changes the existing app. It doesn't start over.

1. Postgres connection. A lib/postgres.ts module opens a connection pool with credentials from the Snowflake secrets that app.yml mounts. The connection uses SSL, which Snowflake Postgres requires.

2. Write routes. New API routes insert and update actions with parameterized queries:

const pool = getPgPool()
await pool.query(
  "INSERT INTO actions (customer_key, assigned_to, action_type, notes) VALUES ($1, $2, $3, $4)",
  [customerKey, assignedTo, actionType, notes]
);

The action list and its KPI cards read from Postgres too, so a new action shows up the moment you save it. A customer can have only one open action: flagging them again shows already flagged instead of creating a duplicate.

3. Mirroring. Postgres changes replicate to Snowflake automatically. ACTION_SUMMARY reads the mirrored $live view. The app never queries it.

4. Notifications. ESCALATION_ALERT runs every minute. When the dynamic table shows open escalations, it writes a row to NOTIFICATIONS. The app polls that table and shows a toast. No email or webhook needed.

5. Manifest. app.yml gains secrets for the Postgres credentials and host, and external_access_integrations for network access to Postgres.

6. Redeploy. The app upgrades in place at the same URL.

Understanding the Updated app.yml

Compare app.yml with the iteration 1 version. Cortex Code added two blocks:

Updated app.yml with secrets and EAI highlighted
New fieldPurpose
secretsMounts Snowflake secrets inside the running app. Each entry maps a name the code uses, such as PG_CREDENTIALS, to a secret object. Credentials never appear in source code.
external_access_integrationsLets the app make outbound network calls. CHURNGUARD_EAI pairs the Postgres network rule (host and port) with the secrets above.

The install, build, run, and ignore fields don't change. Adding these two blocks is all the app needs to reach Postgres.

Key Concepts

  • Snowflake Postgres is managed PostgreSQL inside Snowflake, built for fast row-level inserts and updates from apps. See Snowflake Postgres.
  • Data mirroring copies Postgres changes into Snowflake continuously. The $live view is about 30 seconds behind, with no ETL to build. See Data mirroring.
  • External Access Integrations pair a network rule with secrets so an app can call an outside host. See External network access.
  • Dynamic tables keep a query result up to date. ACTION_SUMMARY refreshes within a minute, so the alert reads totals instead of raw rows. See Dynamic tables.
  • Alerts run a check on a schedule and act when it's true. ESCALATION_ALERT keeps notification logic in Snowflake, not in the app. See Alerts.

Verify

Open the app and run through the workflow:

  1. Find a high-risk customer in the customer table.
  2. Click Flag and choose Escalate as the action type.
  3. Open the Action Board. The new action and its KPI cards appear right away, because both read from Postgres.
  4. Click the status badge to move it from New to In Progress to Resolved.
  5. Wait 1–2 minutes while the data mirrors, the dynamic table refreshes, and the alert runs.
  6. A toast appears for the escalation. Dismiss it.
Retention action form
Action Board with status badges and summary cards

Checkpoint: you can flag a customer, move the action through its statuses, and see an escalation toast. You're ready for iteration 3.

Iteration 3: Make ChurnGuard Intelligent

Ask your data. Get a next step.

In this iteration ChurnGuard gets an agent. You ask a question from any page, and a Cortex Agent explores the data, explains what it finds, and recommends what to do. Overnight, a serverless task stages recommendations, so proposals wait for you in the morning. An MCP server lets other agents, such as the Cortex Code CLI, use ChurnGuard without opening a browser.

Iteration 3 architecture

AI proposes. You approve. Three design choices keep a person in control:

  • The playbook decides what to do. RETENTION_PLAYBOOK holds ordered rules, such as High risk and spend over $7,000 → Offer. The CUSTOMER_RECOMMENDATIONS view applies them, so the same customer always gets the same action and you can see which rule produced it. The agent chooses which customers to look at, and the playbook chooses the action.
  • The agent proposes, and the app writes. The agent can't write actions. Its custom tool stages proposals in PROPOSED_ACTIONS. You review them on the Agent Suggested tab, and approving one goes through the same POST /api/actions route as a manual action.
  • A task stages proposals daily. AGENT_SUGGESTION_TASK calls the staging procedure at 08:00 UTC.

Setup

Commit your iteration 2 work if you haven't already, then merge in iteration 3:

# Commit iteration 2, then merge in iteration 3
git add -A && git commit -m "Iteration 2 done"
git fetch origin && git merge --no-edit origin/iteration-3/ai-app

Step 1 — Rerun grants. This adds the privileges for the agent, the staging procedure, and the task:

snow sql -f scripts/grants.sql --connection quickstart

Step 2 — Create the playbook, recommendations view, proposals table, staging procedure, and daily task:

snow sql -f scripts/iteration-3-setup.sql --connection quickstart

The setup also runs the task once, so proposals are ready right away. See iteration-3-setup.sql for the full SQL.

The playbook starts with five rules:

PriorityWhenAction
1High churn risk and credit rating "High Risk"Escalate
2High churn risk, spend over $7,000Offer
3High churn risk, return rate over 40%Call
4High churn risk (any other)Email
5Medium churn risk, return rate over 25%Email

The Prompts

Iteration 3 uses two prompts, like iteration 2.

1. Semantic view and agent. Open the prompt file 03-agent-setup.md and paste it into Cortex Code Desktop. It uses /agent-studio to create the CHURNGUARD_SV semantic view and the CHURNGUARD_AGENT agent, then tests the agent.

2. Agent in the app. Open the prompt file 03-add-agent.md and paste it into Cortex Code Desktop. It uses /snowflake-apps to add the chat drawer, the Agent Suggested tab, the approve flow, and the MCP server, then redeploys.

What Happens

In Snowflake

1. Semantic view. CHURNGUARD_SV covers CHURN_METRICS, CHURN_TRENDS, CUSTOMER_RECOMMENDATIONS, ACTION_SUMMARY, and NOTIFICATIONS, with synonyms and verified queries. The agent can answer questions such as "which high-risk customers have no open action?"

2. Cortex Agent. CHURNGUARD_AGENT has:

  • Cortex Analyst on the semantic view, to answer data questions with SQL
  • Data to Chart, to answer with charts
  • A custom tool, stage_retention_actions, backed by the STAGE_RETENTION_PROPOSALS procedure, to stage proposals
  • Instructions to take actions only from CUSTOMER_RECOMMENDATIONS and to skip customers who already have an open action

3. Daily proposals. AGENT_SUGGESTION_TASK runs at 08:00 UTC. It stages up to 20 proposals, highest risk and spend first, for customers with no open action and no pending proposal.

In the app

4. Chat in the app. Open the chat drawer from the header or with Cmd+K. The conversation stays open as you move between pages. Each screen also offers a shortcut that carries its context:

  • Customer row: Ask about this customer
  • Segment chart bar: Explain this segment
  • High-risk KPI: Why?
  • Action Board card: Suggest next step
  • Escalation toast: Explain this escalation

An empty drawer suggests prompts for the current page.

5. Streaming answers. An API route streams the agent:run REST API. An answer takes about 30 seconds, so the chat shows a thinking indicator, then each tool call, then the answer with tables and charts. When the agent stages proposals, the chat shows how many and links to the Agent Suggested tab.

6. Agent Suggested tab. The Action Board gains a tab of pending proposals. Each card shows the customer, the action, and the playbook rule behind it. Approve writes the action to Postgres, Dismiss drops it, and Approve All handles every pending proposal. A badge shows how many are waiting. If a customer already has an open action, approving dismisses the proposal and tells you how many were skipped.

7. Threads. The app reuses one agent thread per conversation, so follow-up questions keep their context.

8. MCP server. The app serves an MCP server at /api/mcp with four tools: flag_customer, list_actions, get_customer_risk, and get_churn_summary. Snowflake authenticates every request as your user. The Cortex Code CLI connects with your Snowflake connection, not an API key; other MCP clients are redirected to the Snowflake login page.

Key Concepts

  • Cortex Agents plan, call tools, and answer in a single managed service. See Cortex Agents.
  • A lookup table grounds the agent. Rules in a table make recommendations consistent and easy to explain. Change a threshold in RETENTION_PLAYBOOK and the next answer uses it, with no change to the agent.
  • Custom tools connect an agent to a stored procedure. See Custom tools.
  • The agent:run REST API streams the agent's work and keeps context in threads. See Cortex Agents Run API.
  • Serverless tasks run on managed compute, so there's no warehouse to size or suspend.
  • Model Context Protocol (MCP) lets other agents call your app's tools. See Model Context Protocol.

Verify

Open the app and try the agent:

  1. Open the Action Board and switch to the Agent Suggested tab. You should see the proposals the setup staged.
  2. Click Approve All. The actions move to the main Action Board.
Agent Suggested tab with proposals
  1. Open the chat drawer with Cmd+K.
  2. Ask: "Who are the top 5 high-risk customers by spend and what should we do?"
  3. The agent returns a table of 5 customers, each with an action and the playbook rule behind it.
Agent answering a data question
  1. Ask: "Flag high-risk customers for retention." The agent calls stage_retention_actions, and new proposals appear on the Agent Suggested tab.
  2. Ask: "Show me the monthly churn trend as a chart with readable labels." The agent answers with a chart.
Agent chart response
  1. On the dashboard, open a customer row and choose Ask about this customer. The agent explains that customer's risk and suggests a next step.
Asking the agent from a customer row

Verify the MCP server with the Cortex Code CLI. Add the server and start the CLI. Run this from the churnguard/ folder; snow app open --print-only prints your App URL, and the command adds /api/mcp to it. The --snowflake-connection flag signs each request in as your Snowflake user.

# Add the ChurnGuard MCP server, then start the CLI
cd churnguard
cortex mcp add churnguard "$(snow app open --print-only --connection quickstart)/api/mcp" \
  --type http --snowflake-connection quickstart
cortex mcp reconnect churnguard   # should report 4 tools
cd ..
cortex

Then ask the CLI, one at a time (your results will differ):

Ask the CLITool it callsSample result
Using the churnguard MCP server, list open retention actions.list_actions11 open actions: 10 Offer, 1 Call
Find a high-risk customer with no open action and show their risk.get_churn_summary, get_customer_riskCustomer 56557609: High churn risk, $9,966 spend, 100% return rate. Recommended: Escalate (rule 1)
Flag that customer with the recommended action.flag_customerCreated action 8d701687-… (Escalate, status New). Flag them again and the tool answers already flagged.

Open the Action Board in your browser: the new action is at the top of All actions, and Open escalations goes up by one. Within about a minute, once the mirror syncs and the alert runs, an escalation toast appears on every page.

Checkpoint: the agent answers questions, stages proposals you can approve, and the MCP server works from the CLI. ChurnGuard is complete.

Troubleshooting

Use the queries below, or paste the error into Cortex Code Desktop and ask "Why am I seeing this?"

SymptomLikely causeFix
Insufficient privileges on deployA grant is missing, or the connection's default role isn't the quickstart roleRerun grants.sql as ACCOUNTADMIN, and set role = "SFQUICKSTART_CHURNGUARD_ROLE" for the connection (see Prepare Your Working Directory)
Connection refused or SSL required (iteration 2)The Postgres instance isn't running yetWait for provisioning, then check the instance state
psql hangs (iteration 2)The Postgres instance is suspendedRun ALTER POSTGRES INSTANCE CHURNGUARD_PG RESUME, wait until READY, then retry
psql can't reach Postgres from your machineSetup removed your IP from the network policyAdd CHURNGUARD_PG_LOCAL_IP_RULE back to CHURNGUARD_PG_POLICY (below), and remove it when you're done
Lost the Postgres application passwordIt's shown only once, at CREATERun ALTER POSTGRES INSTANCE CHURNGUARD_PG RESET ACCESS FOR 'application' and update CHURNGUARD_PG_SECRET
The app can't reach PostgresExternal Access Integration or secrets missingCheck CHURNGUARD_EAI and the two Postgres secrets exist
Actions don't reach SnowflakeMirroring runs about 30 seconds behindWait, then check the mirror state is REPLICATING
No toast after an escalationThe dynamic table or alert hasn't refreshedCheck the dynamic table refresh history
The chat shows errors (iteration 3)The agent or semantic view is missingCheck both exist in the quickstart schema
The Agent Suggested tab is emptyThe task hasn't run or is suspendedCheck task history, resume it, or run it now
The MCP server won't connectWrong URL, connection, or roleThe URL must be your App URL plus /api/mcp; your connection test must pass; your role needs USAGE on the app. Use the Cortex Code CLI: other MCP clients are redirected to the Snowflake login

The checks, grouped by area:

-- Grants, Postgres, and network
SHOW GRANTS TO ROLE SFQUICKSTART_CHURNGUARD_ROLE;
SHOW POSTGRES INSTANCES IN ACCOUNT;
SHOW EXTERNAL ACCESS INTEGRATIONS;
SHOW SECRETS IN SCHEMA SFQUICKSTART_CHURNGUARD.PUBLIC;
-- Allow psql from your machine again (if your IP changed, update the rule first:
-- ALTER NETWORK RULE SFQUICKSTART_CHURNGUARD.PUBLIC.CHURNGUARD_PG_LOCAL_IP_RULE SET VALUE_LIST = ('<your-public-ip>/32');)
ALTER NETWORK POLICY CHURNGUARD_PG_POLICY
  ADD ALLOWED_NETWORK_RULE_LIST = ('SFQUICKSTART_CHURNGUARD.PUBLIC.CHURNGUARD_PG_LOCAL_IP_RULE');
-- ...and remove it when you're done
ALTER NETWORK POLICY CHURNGUARD_PG_POLICY
  REMOVE ALLOWED_NETWORK_RULE_LIST = ('SFQUICKSTART_CHURNGUARD.PUBLIC.CHURNGUARD_PG_LOCAL_IP_RULE');

-- Mirroring and the dynamic table
SELECT * FROM CHURNGUARD_MIRROR.PUBLIC.ACTIONS$live LIMIT 5;
CALL SNOWFLAKE.POSTGRES.LIST_MIRRORED_TABLES('churnguard_mirror');
SELECT * FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_REFRESH_HISTORY(
    NAME => 'SFQUICKSTART_CHURNGUARD.PUBLIC.ACTION_SUMMARY'
)) ORDER BY REFRESH_END_TIME DESC LIMIT 5;
-- Agent, semantic view, and the daily task
SHOW AGENTS IN SCHEMA SFQUICKSTART_CHURNGUARD.PUBLIC;
SHOW SEMANTIC VIEWS IN SCHEMA SFQUICKSTART_CHURNGUARD.PUBLIC;
SELECT * FROM TABLE(INFORMATION_SCHEMA.TASK_HISTORY(
    TASK_NAME => 'AGENT_SUGGESTION_TASK'
)) ORDER BY SCHEDULED_TIME DESC LIMIT 5;
SELECT * FROM PROPOSED_ACTIONS ORDER BY PROPOSED_AT DESC LIMIT 10;
SHOW TASKS LIKE 'AGENT_SUGGESTION_TASK';
ALTER TASK AGENT_SUGGESTION_TASK RESUME;   -- if suspended
EXECUTE TASK AGENT_SUGGESTION_TASK;        -- run it now

Merge conflict when moving to the next iteration

A conflict means a file outside churnguard/ was edited. Keep the iteration branch's version:

git checkout --theirs <file> && git add <file> && git commit --no-edit

Anything else

Paste the error into Cortex Code Desktop and ask:

"I'm running the ChurnGuard quickstart and got this error: [paste error]. What's wrong and how do I fix it?"

Cortex Code can run diagnostic queries in your account.

Cleanup

Run this when you're done, including any Build Your Own App prompts in the next section. It removes everything the guide created:

snow sql -f scripts/cleanup.sql --connection quickstart

It drops:

  • The APPLICATION SERVICE
  • The data mirror, its CHURNGUARD_MIRROR database, and its catalog integration
  • The CHURNGUARD_PG Postgres instance
  • CHURNGUARD_EAI, CHURNGUARD_PG_POLICY, and the network rules
  • The SFQUICKSTART_CHURNGUARD database and everything in it
  • The SFQUICKSTART_CHURNGUARD_WH warehouse and SFQUICKSTART_CHURNGUARD_ROLE

The script ends by listing anything left over; every list should be empty.

Warning: Make sure the script finishes. A Postgres instance left running keeps using credits.

Leave SNOWFLAKE_SAMPLE_DATA in place unless you're sure nothing else in your account uses it.

To clean up by hand:

-- Manual cleanup: run these in order
USE ROLE ACCOUNTADMIN;
DROP APPLICATION SERVICE IF EXISTS SFQUICKSTART_CHURNGUARD.PUBLIC.CHURNGUARD;

-- The quickstart role owns the mirror and the Postgres instance
USE ROLE SFQUICKSTART_CHURNGUARD_ROLE;
-- Also drops the CHURNGUARD_MIRROR database and its catalog integration
CALL SNOWFLAKE.POSTGRES.DROP_MIRROR('churnguard_mirror');
DROP POSTGRES INSTANCE IF EXISTS CHURNGUARD_PG;

USE ROLE ACCOUNTADMIN;
DROP INTEGRATION IF EXISTS CHURNGUARD_EAI;
DROP NETWORK POLICY IF EXISTS CHURNGUARD_PG_POLICY;
DROP DATABASE IF EXISTS SFQUICKSTART_CHURNGUARD;   -- includes the network rules
DROP WAREHOUSE IF EXISTS SFQUICKSTART_CHURNGUARD_WH;
DROP ROLE IF EXISTS SFQUICKSTART_CHURNGUARD_ROLE;

If DROP_MIRROR fails, DROP POSTGRES INSTANCE fails too, because the mirror's catalog integration still references the instance. cleanup.sql shows the error and handles both leftovers; by hand, run DROP INTEGRATION IF EXISTS CHURNGUARD_MIRROR_CATALOG_INTEGRATION, and to drop the mirror database, first run GRANT OWNERSHIP ON DATABASE CHURNGUARD_MIRROR TO ROLE ACCOUNTADMIN (it's owned by the snowflake application).

To remove what the guide set up on your machine:

cortex mcp remove churnguard

If you used psql, also remove the CHURNGUARD_PG entries from ~/.pgpass and ~/.pg_service.conf. Then set your connection's default role back to what it was: SFQUICKSTART_CHURNGUARD_ROLE no longer exists.

Conclusion And Resources

What You Learned

You built one app in three iterations, and each one added a kind of app:

  • Explore your data. A dashboard you can pivot and drill into, deployed with one command and no credentials in your code.
  • Run the workflow. Operational writes (fast row inserts and updates) to Snowflake Postgres, mirrored into Snowflake, where a dynamic table and an alert drive notifications.
  • Bring AI into the app. A Cortex Agent grounded in a playbook. It proposes actions, a person approves them, a task stages them daily, and an MCP server lets other agents use the app.

Every iteration followed the same loop:

  1. Describe the outcome in an IDD prompt.
  2. Build: Cortex Code scaffolds or changes the app.
  3. Deploy with snow app deploy to the same URL, with no downtime.
  4. Verify in the browser.
  5. Iterate with another prompt.

To compare your app with the reference, see the solution branch of the companion repo.

Build Your Own App

Keep going. Paste any of these prompts into Cortex Code Desktop and keep extending ChurnGuard. Each one follows the IDD structure, so edit the requirements to fit what you want.

User profiles and role-based views

Goal: Personalize ChurnGuard for each signed-in user.

Requirements:
- Identify the signed-in user from the sf-context-current-user request header
  (or CURRENT_USER() in a query run with the callersRights: true option).
  Snowflake already handles login.
- Show the user's name and role in the header, with a profile menu
- Store preferences (default risk filter, default page) in a USER_PREFERENCES table
- Show "My actions" on the Action Board: actions assigned to the current user
- Only users with the CHURNGUARD_MANAGER role can see Approve All

Constraints:
- Check roles in the API route, not only in the UI
- Keep per-request identity in request scope, never in module-level variables

Output: Redeployed app with profile menu, saved preferences, and role-based approval.

Export at-risk customers

Goal: Let users take a filtered customer list into a spreadsheet.

Requirements:
- Add an "Export CSV" button to the customer table
- Export the rows that match the current filters, not only the current page
- Name the file churnguard-<risk>-<date>.csv

Output: Redeployed app with CSV export.

More ideas to write as IDD prompts in the same shape:

IdeaGoalKey requirements
Edit the playbook in the appLet managers change RETENTION_PLAYBOOK rules without writing SQLA Playbook page to edit, reorder, and turn rules on or off; every change recorded in a PLAYBOOK_AUDIT table; parameterized writes
Weekly churn digestSend the retention team a weekly summary without anyone opening the appA serverless task every Monday at 08:00 UTC; new high-risk customers, open escalations, and resolved actions; email through a notification integration; a Preview digest button
Cohort retention chartShow whether customers stay longer over timeCustomers grouped by first purchase month or quarter; retention by month after; an Explain this cohort agent shortcut
Dark mode and mobile layoutMake ChurnGuard comfortable on any screen, day or nightA remembered dark/light toggle; tables become cards on narrow screens; full-screen chat drawer; WCAG AA contrast in both themes

Related Resources

Updated Oct 19, 2026

This content is provided as is, and is not maintained on an ongoing basis. It may be out of date with current Snowflake instances