Quickstart
Build and run your first complete app with SBT: three tiny tables, two models, one app. Every step verified on SBT 1.0.10.
This guide walks you through building a small but complete app with Seek Build Tools (SBT): three source tables, a SQL model that joins them, a Python model on top, bundled into an app you can run. Each step links to a deeper guide.
Before you start: install SBT on your machine (Setting Up SBT). That's one-time setup; everything here happens inside a project.
1. Create the project
Decide where your projects will live and go there:
mkdir -p ~/sbt-projects && cd ~/sbt-projects Create the project:
sbt init my-first-app This creates:
my-first-app/
├── models/ (with empty placeholder files test.sql, test.yml)
├── modules/
├── sources/ (with empty placeholder file test.yml)
└── requirements.txtDelete the empty test.* placeholder files. Then step inside:
cd my-first-app This matters more than it looks. SBT reads its configuration, models, and sources from the folder you're standing in. Every
sbtcommand in this guide must run from inside the project folder. If a command can't find your files, check where you are withpwd.
Also create one folder init doesn't make, which you'll need in step 6:
mkdir helpers 2. Configure it
Run sbt init-config. It asks you questions and writes the answers to a new file, sbtconf.toml, in your project root. Have two things ready:
Snowflake connection details: account, user (your email), warehouse, database, schema, role. Use your team's standard dev values; ask your lead if you don't have them. Asked for a
private_key? Leave it empty; SSO users don't need one.An Insight Cloud API key. Create one per API Keys.
Then confirm everything connects (a browser window opens for Okta login; that's normal):
sbt check-conn sbtconf.toml holds credentials: never commit it or paste its contents anywhere. For every setting explained, extra environments, and switching between them, see Configuring Your Project (sbtconf.toml).
3. Create practice data (optional but recommended)
If you have your own sandbox database, make three tiny tables to learn with. In a Snowflake worksheet:
USE ROLE SEEK_INSIGHTS_ADMIN;
USE WAREHOUSE SNOWFLAKE_LEARNING_WH;
CREATE OR REPLACE TABLE SANDBOX_<YOURNAME>.APP.PRODUCTS (
product_id INT, product_name VARCHAR, category VARCHAR, unit_price NUMBER(6,2)
);
INSERT INTO SANDBOX_<YOURNAME>.APP.PRODUCTS VALUES
(1,'Sea Salt Chips','Chips',2.99),(2,'BBQ Chips','Chips',2.99),
(3,'Gummy Bears','Candy',1.25),(4,'Chocolate Bar','Candy',1.75),
(5,'Cola','Drinks',2.99),(6,'Sparkling Water','Drinks',1.99);
CREATE OR REPLACE TABLE SANDBOX_<YOURNAME>.APP.STORES (
store_id INT, store_name VARCHAR, region VARCHAR
);
INSERT INTO SANDBOX_<YOURNAME>.APP.STORES VALUES
(10,'Downtown','East'),(20,'Airport','East'),(30,'Suburbs','West');
CREATE OR REPLACE TABLE SANDBOX_<YOURNAME>.APP.SNACK_SALES (
sale_date DATE, store_id INT, product_id INT, units INT
);
INSERT INTO SANDBOX_<YOURNAME>.APP.SNACK_SALES VALUES
('2026-08-03',10,1,5),('2026-08-03',10,3,12),('2026-08-03',20,5,9),
('2026-08-03',30,4,7),('2026-08-04',10,2,6),('2026-08-04',20,3,15),
('2026-08-04',20,6,8),('2026-08-04',30,1,4),('2026-08-05',10,5,11),
('2026-08-05',20,4,10),('2026-08-05',30,6,6),('2026-08-05',30,2,9),
('2026-08-06',10,4,8),('2026-08-06',20,1,7),('2026-08-06',30,3,14),
('2026-08-06',30,5,10);One fact table (sales), two dimension tables (products, stores). That's the shape of real apps.
4. Define your sources
Sources are the database tables your app reads, declared in YAML. Create sources/sources.yml:
sources:
- name: app
description: "Practice tables for the quickstart"
database: SANDBOX_<YOURNAME>
tables:
- name: products
- name: stores
- name: snack_salesThe group
nameis not a label. It must match the schema your tables live in. These tables are in theAPPschema, so the group is namedapp. Models reference tables asgroup.table(reallyschema.table), likeapp.products.
More source options, including bundled sources: Sources.
5. Create your first model (SQL)
A model is one unit of logic that produces one output table. Two files. First models/config.yml:
models:
- name: revenue_by_region
config:
language: sql
materialized: table
type: outputtype is required: output is the normal kind (one table per app variant). The other types, mapping and shared, are for advanced sharing; see Model Reuse.
Then the model itself, models/revenue_by_region.sql. SQL models reference sources through the run context, no quotes inside the braces:
select
st.region,
p.category,
sum(s.units) as units,
sum(s.units * p.unit_price) as revenue
from {seek.sources[app.snack_sales]} s
join {seek.sources[app.products]} p on p.product_id = s.product_id
join {seek.sources[app.stores]} st on st.store_id = s.store_id
group by st.region, p.categoryTest it:
sbt run-model revenue_by_region Local runs write with a _test suffix (you'll see target_name='revenue_by_region_test' in the output), so they never collide with production tables. Expect about 10 seconds of startup overhead per run; that's fixed cost, not your data being slow.
More: Models · Helpers & the Run Context · SBT Command References
6. Add a Python model
Python models define everything inline with the @model decorator; no YAML entry needed. seek.models['name'] gives you a table name, which you load through session. Create models/region_share.py:
from sbt.models import model
from snowflake.snowpark.functions import col, sum as sum_, round as round_
@model(name='region_share', materialized='table', type='output')
def region_share(session, seek):
revenue = session.table(seek.models['revenue_by_region'])
total = revenue.agg(sum_(col('REVENUE'))).collect()[0][0]
return revenue.with_column('PCT_OF_TOTAL', round_(col('REVENUE') * 100 / total, 1))Test it: sbt run-model region_share. SBT automatically resolves revenue_by_region to your _test table from step 5.
(If VS Code underlines the imports with "could not be resolved," press Cmd+Shift+P, run "Python: Select Interpreter," and pick Python 3.11. Cosmetic only; the run works either way.)
7. Bundle both models into an app
An app is a bundle of models that run together. First, apps need a freshness helper: a query returning a single date that tells the platform how new your data is. Create helpers/fresh.sql:
select max(sale_date) as date from {seek.sources[app.snack_sales]} Then add the app to the bottom of models/config.yml:
apps:
- name: my_first_app
requires:
- region_share
config:
sigma_workbook_id: your_sigma_workbook_id
sigma_db_attribute: test_db
freshness_query: fresh
publisher: "your_publisher_key"
version: "1"Line by line: you only list region_share because SBT follows the dependency chain and pulls in revenue_by_region automatically. sigma_workbook_id connects the app to its Sigma dashboard (Data Visualizations with Sigma); a placeholder value is fine for local runs. freshness_query: fresh points at helpers/fresh.sql by filename. publisher is your org's publisher key, created when your org enables the Publisher Portal (Org Management > General > Platform Addons); any string works for local runs, but publishing needs the real key. version is your app's version string.
Full app config options: Apps.
8. Run it
sbt run-app my_first_app SBT runs both models in dependency order, then prints your data's freshness date. If you see two green "Success!" boxes: congratulations, you've built a working app.
Where to go next
Make the dashboard: Data Visualizations with Sigma
Get it onto Insight Cloud: App Publishing Lifecycle and Prerequisites for App Installs. The commands:
build-app,publish-app,install-app, orfull-rebuildfor all three at once.Go deeper on building: Schemas for data validation, Model Reuse for freshness and shared models
The big picture: App & Data Lifecycle