Skip to content

dbt Models

This page documents the dbt transformation models in the XO-Data platform. dbt handles the BRONZE → SILVER → GOLD transformation pipeline after xo-foundry loads raw data.

dbt project location: apps/dbt/dbt_xo_models/

Model Lineage: Warby Parker

flowchart TD
    B1[BRONZE\nGLADLY_CONTACT_TIMESTAMPS] --> S1[SILVER\ncontact_timestamps]
    B2[BRONZE\nGLADLY_CONVERSATION_TIMESTAMPS] --> S2[SILVER\nconversation_timestamps]
    B3[BRONZE\nGLADLY_WORK_SESSIONS] --> S3[SILVER\nwork_sessions]
    B4[BRONZE\nGLADLY_AGENT_DURATIONS] --> S4[SILVER\nagent_durations]
    SEED[SEED\nai_agents.csv] --> RPT
    ROSTER[OPERATIONS.BAMBOOHR\nROSTER_WARBYPARKER\n⚠️ temporary bridge] --> FCT

    S1 --> INT[GOLD int\nint_contacts_hold]
    S1 --> FCT[GOLD fct\nfct_contacts]
    INT --> FCT
    S3 --> AGG[GOLD agg\nagg_agent_daily_contacts]
    FCT --> AGG
    AGG --> RPT[GOLD rpt\nrpt_email_daily\nview]

Temporary roster bridge

fct_contacts currently JOINs OPERATIONS.BAMBOOHR.ROSTER_WARBYPARKER. This is a temporary bridge while CORE_DB.SILVER.ROSTER_WARBYPARKER is being set up per ADR 010.

Silver Models

Silver models are incremental append-only — they accumulate history from Bronze tables that are cleared on each run (batch_replace strategy).

contact_timestamps

Property Value
Target WBP_DB.SILVER.CONTACT_TIMESTAMPS
Source WBP_DB.BRONZE.GLADLY_CONTACT_TIMESTAMPS
Grain 1 row per contact timestamp event
Materialization Incremental table
Unique key RECORD_KEY
Columns 36 Gladly API columns + 7 xo-foundry metadata + 2 dbt audit

Preserves the complete history of contact-level events from the Gladly ContactTimestampsReport API.

conversation_timestamps

Property Value
Target WBP_DB.SILVER.CONVERSATION_TIMESTAMPS
Source WBP_DB.BRONZE.GLADLY_CONVERSATION_TIMESTAMPS
Grain 1 row per conversation timestamp event
Materialization Incremental table
Unique key RECORD_KEY (composite of 10 columns)
Columns 21 Gladly API columns + 7 metadata + 2 audit

Preserves conversation-level event history from ConversationTimestampsReport.

work_sessions

Property Value
Target WBP_DB.SILVER.WORK_SESSIONS
Source WBP_DB.BRONZE.GLADLY_WORK_SESSIONS
Grain 1 row per agent work session
Materialization Incremental table
Unique key RECORD_KEY (WORK_SESSION_ID)
Columns 24 Gladly API columns + 7 metadata + 2 audit

Preserves work session history from WorkSessionsReportV3. Filters out unrouted contacts (null WORK_SESSION_ID).

agent_durations

Property Value
Target WBP_DB.SILVER.AGENT_DURATIONS
Source WBP_DB.BRONZE.GLADLY_AGENT_DURATIONS
Grain 1 row per agent status span
Materialization Incremental table
Unique key RECORD_KEY (composite of AGENT_ID + START_TIME + TYPE)
Columns 9 Gladly API columns + 7 metadata + 2 audit

Preserves agent duration history from AgentDurationsReportV2.

Gold Models

Gold models add business enrichment — roster JOINs, aggregations, and report-ready views.

int_contacts_hold (intermediate)

Property Value
Target Ephemeral (materialized inline)
Source contact_timestamps
Purpose Hold duration calculation using LAG() window function
Grain 1 row per contact with computed hold time

Shared by fct_contacts and agg_agent_daily_contacts. Replaces OPERATIONS.STAGING.STAGE_WARBYPARKER_CONTACTS_HOLD.

fct_contacts

Property Value
Target WBP_DB.GOLD.FCT_CONTACTS
Source contact_timestamps, int_contacts_hold, OPERATIONS.BAMBOOHR.ROSTER_WARBYPARKER
Grain 1 row per contact
Materialization Table

Master contacts fact table with roster enrichment. Tracks contact status, queue times, handle times, agent attribution, and hold durations.

Replaces: OPERATIONS.AGGREGATE.WARBYPARKER_CONTACTS_MODEL

agg_agent_daily_contacts

Property Value
Target WBP_DB.GOLD.AGG_AGENT_DAILY_CONTACTS
Source work_sessions, fct_contacts
Grain 1 row per agent × date × channel × direction × status × inbox
Materialization Table

Daily agent-level aggregation with roster enrichment and tenure bucketing. Reusable building block for multiple reports.

Replaces: OPERATIONS.AGGREGATE.WARBYPARKER_AGENT_DAILY_CONTACTS

rpt_email_daily

Property Value
Target WBP_DB.GOLD.RPT_EMAIL_DAILY
Source agg_agent_daily_contacts, ai_agents seed
Grain 1 row per agent × date
Materialization View (zero storage)
Tableau-ready Yes

Daily email performance report for AI-enabled agents. Tracks email volume, handle times, emails per hour, SLA compliance, and AI wave categorization (pre/post AI enablement date).

Replaces: OPERATIONS.AGGREGATE.WARBYPARKER_RPT_EMAIL_DAILY

Key columns:

Column Description
DATE Date of agent email activity
AGENT_NAME Agent name
TOTAL_EMAILS Total email contacts handled
AVG_HANDLE_TIME_MIN Average contact handle time (minutes)
EMAILS_PER_HOUR Contacts fulfilled per active hour
SLA_MET_PCT Percentage of emails meeting SLA
WAVE_PERIOD Pre-Wave or Post-Wave relative to agent's AI enablement date

Seeds

ai_agents

Property Value
Location seeds/warbyparker/ai_agents.csv
Target WBP_DB.GOLD.AI_AGENTS
Purpose AI wave enablement dates per agent
Columns AGENT_NAME, AI_ENABLED_DATE

Used by rpt_email_daily to compute WAVE_PERIOD — whether a given date is before or after the agent's AI enablement date.

Environment Configuration

The dbt project switches between dev and prod via an environment variable:

# dbt_project.yml
vars:
  environment: "{{ env_var('ENVIRONMENT', 'dev') }}"

models:
  dbt_xo_models:
    silver:
      warbyparker:
        +database: "{{ 'WBP_DB' if env_var('ENVIRONMENT', 'dev') == 'prod' else 'WBP_DB_DEV' }}"
    gold:
      warbyparker:
        +database: "{{ 'WBP_DB' if env_var('ENVIRONMENT', 'dev') == 'prod' else 'WBP_DB_DEV' }}"
Environment Database Set via
dev (default) WBP_DB_DEV Default
prod WBP_DB Airflow Variable ENVIRONMENT=prod

Running dbt

# From apps/dbt/dbt_xo_models/
# Run all models
dbt run --select tag:warbyparker

# Run only Silver
dbt run --select silver.warbyparker.*

# Run only Gold
dbt run --select gold.warbyparker.*

# Run specific model + its downstream dependencies
dbt run --select fct_contacts+

# Seed AI agents lookup
dbt seed --select warbyparker.ai_agents

# Full refresh (rebuilds Silver from Bronze history)
dbt run --full-refresh --select tag:warbyparker

Full refresh safety

Because Bronze uses batch_replace (ADR 011), Bronze tables retain all historical data. Running dbt --full-refresh on Silver incremental models correctly rebuilds Silver from the complete Bronze history.

Airflow Integration

dbt runs are orchestrated by Astronomer Cosmos, integrated into the warbyparker_gladly_daily DAG:

warbyparker_gladly_daily
├── extract_contact_timestamps
├── extract_conversation_timestamps
├── extract_work_sessions
├── extract_agent_durations
├── [stage tasks]
├── [load to bronze tasks]
└── dbt_transform (DbtTaskGroup)
    ├── silver.contact_timestamps
    ├── silver.conversation_timestamps
    ├── silver.work_sessions
    ├── silver.agent_durations
    ├── gold.int_contacts_hold
    ├── gold.fct_contacts
    ├── gold.agg_agent_daily_contacts
    └── gold.rpt_email_daily

DAG config: apps/airflow/xo-pipelines/dags/configs/warbyparker-gladly-daily.yaml