Skip to content

RPT Email Daily

View: OPERATIONS.AGGREGATE.WARBYPARKER_RPT_EMAIL_DAILY Materialization: View (zero storage) Database: OPERATIONS (legacy — not medallion)

Overview

Daily email performance metrics for AI-enabled Warby Parker agents. Each row represents one agent's aggregate email activity for a calendar day. The view is the direct data source for the wbp_xoos_analytics_push Airflow DAG, which queries it every morning to push daily and weekly metrics to the XOOS analytics ingest API.

Audience: XOOS platform, operations analytics, Tableau dashboards, QA/coaching workflows.


Data Sources

# Source Schema Type Purpose
1 WARBYPARKER_CONTACT_TIMESTAMPS_HISTORICAL OPERATIONS.STAGING Bronze table Gladly contact event timestamps — drives email_messages CTE
2 WARBYPARKER_WORK_SESSIONS_HISTORICAL OPERATIONS.STAGING Bronze table Gladly work session records — drives work_session_handle_time CTE
3 WARBYPARKER_AGENT_DURATIONS_REPORT_HISTORICAL OPERATIONS.STAGING Bronze table Gladly agent status durations — drives active_time CTE
4 WARBYPARKER_CONTACTS_MODEL OPERATIONS.AGGREGATE Dynamic Table Contact-level metrics model — drives email_contacts and work_session_handle_time CTEs
5 ROSTER_WARBYPARKER OPERATIONS.BAMBOOHR Dynamic Table BambooHR roster — provides EID, ACCOUNT_EMAIL; filters to XO agents

Dependency Flowchart

flowchart LR
    subgraph Bronze["Bronze Tables — OPERATIONS.STAGING"]
        B1["WARBYPARKER_CONTACT_TIMESTAMPS_HISTORICAL"]
        B2["WARBYPARKER_WORK_SESSIONS_HISTORICAL"]
        B3["WARBYPARKER_AGENT_DURATIONS_REPORT_HISTORICAL"]
    end

    subgraph StagingDT["Staging Dynamic Tables — OPERATIONS.STAGING"]
        S1["STAGE_WARBYPARKER_CONTACTS_QUEUED"]
        S2["STAGE_WARBYPARKER_CONTACTS_ENDED"]
        S3["STAGE_WARBYPARKER_CONTACTS_ACCEPTED"]
        S4["STAGE_WARBYPARKER_CONTACTS_WORK_SESSIONS"]
        S5["STAGE_WARBYPARKER_CONTACTS_HOLD"]
        S6["STAGE_WARBYPARKER_CONTACTS_MESSAGES"]
        S7["STAGE_WARBYPARKER_CONTACTS_EVENTS"]
        S8["STAGE_ROSTER_WARBYPARKER"]
    end

    subgraph AggLayer["Aggregate / BambooHR Layer"]
        A1["WARBYPARKER_CONTACTS_MODEL\n(OPERATIONS.AGGREGATE — Dynamic Table)"]
        A2["ROSTER_WARBYPARKER\n(OPERATIONS.BAMBOOHR — Dynamic Table)"]
    end

    subgraph Output["Output — OPERATIONS.AGGREGATE"]
        V["WARBYPARKER_RPT_EMAIL_DAILY\n(View)"]
    end

    B1 --> S1 & S2 & S3 & S5 & S6 & S7
    B2 --> S4
    S1 & S2 & S3 & S4 & S5 & S6 & S7 --> A1
    A2 --> A1
    S8 --> A2

    A1 -->|"email_contacts CTE\nwork_session_handle_time CTE"| V
    B1 -->|"email_messages CTE"| V
    B2 -->|"work_session_handle_time CTE\n(direct join)"| V
    B3 -->|"active_time CTE"| V
    A2 -->|"LEFT JOIN"| V

Dependency Reference

# Object Type Schema SQL File Purpose
1 WARBYPARKER_RPT_EMAIL_DAILY View OPERATIONS.AGGREGATE operations/aggregate/R__warbyparker_rpt_email_daily.sql Output — daily email metrics per AI agent
2 WARBYPARKER_CONTACTS_MODEL Dynamic Table OPERATIONS.AGGREGATE operations/aggregate/R__warbyparker_contacts_model.sql Contact-level model with handle times, SLA, routing metrics
3 ROSTER_WARBYPARKER Dynamic Table OPERATIONS.BAMBOOHR operations/bamboohr/R__roster_warbyparker.sql Current roster snapshot — provides EID, ACCOUNT_EMAIL, GLADLY_NAME
4 WARBYPARKER_CONTACT_TIMESTAMPS_HISTORICAL Bronze Table OPERATIONS.STAGING — (raw landing) Gladly contact event timestamps; root source for 6 of 7 staging DTs
5 WARBYPARKER_WORK_SESSIONS_HISTORICAL Bronze Table OPERATIONS.STAGING — (raw landing) Gladly work session records; root source for STAGE_*_WORK_SESSIONS
6 WARBYPARKER_AGENT_DURATIONS_REPORT_HISTORICAL Bronze Table OPERATIONS.STAGING — (raw landing) Gladly agent status durations; root source for active_time CTE
7 STAGE_WARBYPARKER_CONTACTS_QUEUED Dynamic Table OPERATIONS.STAGING operations/staging/R__warbyparker_contacts_queued.sql QUEUED events per contact; primary grain for contacts model
8 STAGE_WARBYPARKER_CONTACTS_ENDED Dynamic Table OPERATIONS.STAGING operations/staging/R__warbyparker_contacts_ended.sql ENDED/FULFILLED events per contact
9 STAGE_WARBYPARKER_CONTACTS_ACCEPTED Dynamic Table OPERATIONS.STAGING operations/staging/R__warbyparker_contacts_accepted.sql ACCEPTED events and IVR metadata per contact
10 STAGE_WARBYPARKER_CONTACTS_WORK_SESSIONS Dynamic Table OPERATIONS.STAGING operations/staging/R__warbyparker_contacts_work_sessions.sql Work session handle times and SLA per contact
11 STAGE_WARBYPARKER_CONTACTS_HOLD Dynamic Table OPERATIONS.STAGING operations/staging/R__warbyparker_contacts_hold.sql Hold events and total hold time per contact
12 STAGE_WARBYPARKER_CONTACTS_MESSAGES Dynamic Table OPERATIONS.STAGING operations/staging/R__warbyparker_contacts_messages.sql Message counts (agent and customer) per contact
13 STAGE_WARBYPARKER_CONTACTS_EVENTS Dynamic Table OPERATIONS.STAGING operations/staging/R__warbyparker_contacts_events.sql Routing event counts (offered, accepted, missed, transferred) per contact
14 STAGE_ROSTER_WARBYPARKER Dynamic Table OPERATIONS.STAGING operations/staging/R__stage_roster_warbyparker.sql Staging layer for roster — all historical snapshots; ROSTER_WARBYPARKER selects latest

Schedule & Refresh

Property Value
Airflow DAG wbp_xoos_analytics_push
Schedule 0 7 * * 1-6 — 7 AM EST, Monday–Saturday
Time window Previous day's data (logical_date - 1, or target_date param override)
Timezone America/New_York
Retries 2 × 5 min
DAG file apps/airflow/xo-pipelines/dags/dag_wbp_xoos_analytics.py
Upstream refresh Dynamic Tables use target_lag = DOWNSTREAM — refreshed on-query by Snowflake

Dynamic Table latency note: WARBYPARKER_CONTACTS_MODEL and ROSTER_WARBYPARKER both use target_lag = DOWNSTREAM. This means they are refreshed lazily by Snowflake when the view is queried by the DAG, not on a fixed schedule. Freshness depends on when the upstream HISTORICAL Bronze tables were last loaded.


Column Reference

Column Source Description
DATE email_messages CTE Calendar date of email activity
AGENT_NAME Gladly initiator_agent_name Agent who sent the emails
EID ROSTER_WARBYPARKER.EID BambooHR employee ID (nullable — roster LEFT JOIN)
ACCOUNT_EMAIL ROSTER_WARBYPARKER.ACCOUNT_EMAIL Agent's work email address (nullable)
ACTIVE_TIME_MIN active_time CTE Total minutes agent was in ACTIVE status (attendance proxy)
EMAILS_SENT email_messages CTE Count of CONTACT/MESSAGE_SENT events (channel=EMAIL, initiator=AGENT)
CONTACTS_WORKED email_messages CTE Distinct contact IDs the agent sent a message on
CONVERSATIONS_WORKED email_messages CTE Distinct conversation IDs the agent sent a message on
CONTACTS_FULFILLED email_contacts CTE Contacts fulfilled by this agent (anchored on fulfilled_at)
TOTAL_CONTACT_HANDLE_TIME_MIN email_contacts CTE Sum of total_contact_handle_time_sec / 60 across fulfilled contacts
CONTACT_AHT_MIN Computed total_contact_handle_time_min / contacts_fulfilled (contact-level AHT)
TOTAL_CONTACT_ACW_MIN email_contacts CTE Sum of after_contact_time_sec / 60 across fulfilled contacts
TOTAL_AGENT_HANDLE_TIME_MIN work_session_handle_time CTE Sum of work session handle_time_in_sec / 60 for agent's own sessions
AGENT_AHT_MIN Computed total_agent_handle_time_min / contacts_fulfilled (work-session-level AHT)
TOTAL_AGENT_ACW_MIN work_session_handle_time CTE Sum of work session after_contact_time_in_sec / 60
EMAILS_PER_HOUR Computed contacts_fulfilled / (active_time_min / 60.0) — throughput metric
TOTAL_QUEUED_TO_FULFILLED_MIN email_contacts CTE Sum of queued_to_fulfilled_sec / 60 — total queue wait time
SLA_MET email_contacts CTE Count of contacts where sla_met = TRUE
SLA_ELIGIBLE email_contacts CTE = contacts_fulfilled — denominator for SLA %
AGENT_CATEGORY Hardcoded Always 'AI Enabled Agent' (view scoped to AI agents only)
AGENT_WAVE ai_agents CTE 'Wave 1' or 'Wave 2' — AI enablement cohort
WAVE_PERIOD Computed 'Post-Wave' if date ≥ wave enablement date, else 'Pre-Wave'

Metrics Computed

Metric Formula Notes
CONTACT_AHT_MIN total_contact_handle_time_min / NULLIF(contacts_fulfilled, 0) Contact-level AHT — includes all agents' handle time on the contact
AGENT_AHT_MIN total_agent_handle_time_min / NULLIF(contacts_fulfilled, 0) Agent-level AHT — only this agent's work session time
EMAILS_PER_HOUR contacts_fulfilled / NULLIF(active_time_min / 60.0, 0) Throughput: fulfilled emails per hour of active time
WAVE_PERIOD CASE WHEN wave='Wave 1' AND date >= '2026-01-21' THEN 'Post-Wave' WHEN wave='Wave 2' AND date >= '2026-02-02' THEN 'Post-Wave' ELSE 'Pre-Wave' END Pre/post AI enablement classifier
SLA % SLA_MET / SLA_ELIGIBLE Compute in BI layer — denominator = contacts_fulfilled

AHT sources: Two AHT columns exist because Gladly has two grains — contact-level handle time (summed across all agents who touched the contact) and work-session-level handle time (this agent's sessions only). AGENT_AHT_MIN is generally preferred for per-agent performance.


AI Agents in Scope

The view uses an INNER JOIN on a hardcoded ai_agents CTE — only the 17 agents below appear in results regardless of other activity.

Wave 1 — Enabled 2026-01-21

Agent
Angelica Madrid
Aubrey Sombilon
Dianne Grace Emar
Kyna Montorio
Mariel Perez
Michael Louis Cavile
Myrna Gabion
Rodmer De Los Reyes
Sanshe Surmieda
Terence John Haro

Wave 2 — Enabled 2026-02-02

Agent
Anjanette Baytos
Carlo Gian Chin
Fritz Joshua Fernandez
Jeffrey Sormillo
Joseph Torrena
Raffy Tampos
Shaina Rica Palmares

Known Limitations

  • Hardcoded agent list: The ai_agents CTE is a VALUES literal in the view SQL. Adding or removing agents requires editing R__warbyparker_rpt_email_daily.sql and redeploying via schemachange. A TODO in the SQL notes the future intent to replace this with a roster AA_STATUS = 'Enabled' filter once that field is reliably populated.
  • HISTORICAL table latency: The three Bronze HISTORICAL tables (CONTACT_TIMESTAMPS, WORK_SESSIONS, AGENT_DURATIONS) are loaded by separate Airflow pipelines on their own schedules. The wbp_xoos_analytics_push DAG at 7 AM EST assumes upstream data for the previous day is already present.
  • Dynamic Table DOWNSTREAM lag chain: WARBYPARKER_CONTACTS_MODEL and ROSTER_WARBYPARKER are both target_lag = DOWNSTREAM. They refresh lazily when queried. If Bronze tables are delayed, the Snowflake refresh triggered by the DAG query will read stale data silently — no error is raised.
  • No AA_STATUS filtering yet: ROSTER_WARBYPARKER includes all active roster rows. The view currently uses the hardcoded agent list as a proxy for "AI-enabled." Future work (see SQL TODO) will filter by AA_STATUS.
  • EID / ACCOUNT_EMAIL can be null: The roster join is a LEFT JOIN on GLADLY_NAME. Agents not matched in the roster will have null for these columns, which breaks the XOOS RECORD_KEY (EID || date || client_id). Monitor for null EIDs in the push task output.
  • CONTACTS_WORKEDCONTACTS_FULFILLED: CONTACTS_WORKED counts contacts where the agent sent at least one message. CONTACTS_FULFILLED counts contacts the agent was the fulfilling agent. An agent may message a contact without being the one to fulfill it.