Today, in one afternoon, I built a guest 360 for a hotel group. I started with two exports, one from Salesforce and one from MongoDB, and finished with a React app the front desk can open on a phone. Here is what I started with, what I did, and the Snowflake features it took.
Where I started
The CRM lived in Salesforce: one row per guest, one row per stay. Names, tiers, folio totals, the stays on record.
GUEST_ID FULL_NAME TIER LIFETIME_GBP STAYS LAST_STAY
G-1002 Elena Marchetti VIC 61400 14 2026-05-02
G-1044 David Okonkwo Gold 38150 11 2026-06-18
The concierge notes lived in MongoDB, as free text documents, one per interaction.
{
"guest_id": "G-1002",
"property": "Meridian Lac Léman",
"note": "Ruhiges Zimmer, obere Etage, federfreies Kissen. Frühstück 7 Uhr.",
"author": "Henri B.",
"logged_at": "2025-12-24"
}
Two systems, two shapes. Neither agrees on what a guest is, and the note is in German.
The pipeline, and I made it automatic
I clean and join the two sources in layers, raw to staging to gold, so when a number looks wrong I know which layer to check. The part that matters is that every layer is a dynamic table. I write the query once, set a target lag, and Snowflake keeps it fresh. When Salesforce syncs a new stay, or a new note lands from MongoDB, the 360 updates itself. No schedule to babysit, no orchestration to write.
create or replace dynamic table gld_guest_360
target_lag = '1 hour'
warehouse = concierge_wh
as
select
g.guest_id,
g.full_name,
sum(s.folio_total_gbp) as lifetime_spend_gbp,
count(distinct s.stay_id) as total_stays,
count(distinct s.property_id) as properties_visited,
sum(s.nights) as total_nights
from stg_salesforce_guests g
left join stg_salesforce_stays s on g.guest_id = s.guest_id
group by 1, 2;
Every table downstream is the same idea, and I did not write them by hand. I described what I wanted to Cortex Code, Snowflake’s coding agent, and it wrote the SQL with me. I still read every line. One of those tables reads the notes.
Reading the text with AI
The notes are the useful part, and they are messy. Snowflake has AI functions you call inside a SELECT, so I never move the data. AI_TRANSLATE puts the German note into English. AI_SENTIMENT scores how it feels. AI_COMPLETE reads a guest’s notes and returns their preferences as clean fields.
create or replace dynamic table gld_guest_preferences
target_lag = '1 hour'
warehouse = concierge_wh
as
select
guest_id,
ai_complete('mistral-large2',
'Return room, bed, dining, do_not and allergies from these notes: ' ||
listagg(ai_translate(note, '', 'en'), ' | ')
) as preferences
from raw_mongodb_notes
group by 1;
The app
The same agent built the front end. I gave it the gold tables and told it what I wanted, and it built the React app that reads them. The Guest Portrait shows one guest across every property, whether they take an upgrade, and what to get right before they arrive.
That is it
One afternoon, two sources, one app. The Snowflake features it took:
- Dynamic tables, for a pipeline that refreshes itself as new data lands
AI_TRANSLATE,AI_SENTIMENT,AI_CLASSIFYandAI_COMPLETE, to read and structure the notes in SQL- Cortex Code, to write the SQL and build the React app
No frills.