Frontier
Viva Insights Schema Documentation
Use this guidance to inspect Viva Insights export structures, plan joins and check for common data-quality issues.
For authoritative and up-to-date metric definitions and column references, see:
On column names: Column names can vary by tenant and localization settings (e.g., US vs UK English). Using
import_query()from thevivainsightslibrary (R or Python) is recommended because it replaces spaces and special characters with underscores, eliminating common issues when referring to column names in code. Useextract_hr()to programmatically identify the HR / organizational attribute columns available in your data.
On this page:
- Consumption and GitHub queries covers documented and illustrative inputs and supported AI-guided workflows.
- Person Query concepts explains panel structure, licensing logic and common checks.
- Purview Audit Log concepts covers event structure, the AuditData column and data quality.
- Join patterns covers Person Query joins with Purview audit logs, external HR data and licence records.
- Common pitfalls lists analytical mistakes and data-quality checks.
Related resources:
- Prompt Library has prompts for coding agents.
- Frontier overview introduces the available assets and setup.
- Joining Viva Insights queries covers Person, Consumption, GitHub and Agent schemas, export setup and person-week joins.
- vivainsights R package
- vivainsights Python package
Consumption and GitHub queries
These are not interchangeable with a Person Query.
For the four query families and their native keys, use the join tutorial. It is the current cross-query reference and supersedes older integration guidance in this repository. Export all selected queries grouped by week where possible. It also supports configured daily facts aligned to a weekly Person Query. Other export periods require a separately defined integration.
| Source | Contract boundary | Guided journey |
|---|---|---|
M365 facts include person, service, spending policy and period. Join people metadata through PeopleHistoricalId. Credits and sessions do not establish tokens, task types or monetary value. |
Build a Consumption dashboard | |
| Activity summary plus four dimensional breakdown files. Synthetic demos use export headers, but a real GitHub dashboard adapter is not verified in v1. | Build a Developer Experience dashboard | |
| Person-agent-week activity, with optional agent metadata. Aggregate sessions across agents before joining to the Person Query. | Agent schema and joins |
Use the tutorial for current keys, aggregation, missing-row defaults and privacy rules. Assume supplied Viva Insights queries are complete within their configured population and date window. Daily exports omit inactive person-days by default, so those missing activity rows contribute zero, without establishing licence status. Keep unavailable queries, out-of-scope periods and null cells separate. The historical query contracts retain dated layouts and analytical context. See the public Consumption documentation for official product documentation. Missing fields exclude panels; agents must not generate synthetic replacements in a real-data run.
Person Query concepts
Panel structure
Person query data follows a person-time panel format:
- Each row represents one person for one time period (typically one week).
- Primary key is the combination of
(PersonId, MetricDate). Each person appears once per time period. - Granularity is usually person-week (one row per person per week), but some exports use person-day.
- A typical export contains
N_persons × N_periodsrows. For example, 5,000 employees over 12 weeks produces approximately 60,000 rows.
Confirm the configured date grouping, then check the gaps between MetricDate
values. Sparse observed dates alone do not establish the export’s cadence.
Column categories
Person Query exports typically contain these categories of columns. Use extract_hr() and pattern matching (columns containing “Copilot”) to identify the available columns, as exact names vary by tenant.
| Category | Description |
|---|---|
| Identifiers | PersonId (anonymized), MetricDate (period start date) |
| HR attributes | Organisational metadata (org, function, level, region, etc.), configurable per tenant |
| Collaboration metrics | Meeting hours, email hours, chat hours, focus hours, network size, etc. |
| Copilot metrics | Selected activity metrics with product-specific definitions. Licensed Copilot activity and unlicensed Copilot Chat are separate families. |
Licensed vs. unlicensed vs. active
This distinction is critical for any Copilot adoption analysis:
| Status | Meaning | How to detect |
|---|---|---|
| Unlicensed | User does not have a Copilot license | Explicit licence evidence for the period |
| Licensed but inactive | User has a license but did not use Copilot | Confirmed licence and zero activity, including omitted inactive rows within a supplied query’s scope |
| Active | Observed use of the selected Copilot metric | Verified activity metric > 0; licence status is a separate attribute |
| Unknown | Eligibility or observation coverage is unresolved | Retain separately; do not infer from absence |
Critical: A null cell in an existing row is different from an omitted inactive row. Neither an activity zero nor a null establishes licensing. Use documented enabled-days or licence records for eligibility. Assume supplied Viva Insights queries are complete within their configured scope unless a known issue applies. If a denominator is unknown, omit adoption rates.
Purview Audit Log concepts
Purview audit logs capture user activity events across Microsoft 365 services, including Copilot interactions. They supplement person query data with granular event-level detail.
For authoritative documentation, see Audit log activities on Microsoft Learn.
Key characteristics
| Property | Details |
|---|---|
| Granularity | One row per event (not aggregated) |
| Export format | Typically CSV or JSON from the Purview compliance portal |
| Timestamps | Always in UTC |
| User identifiers | UPN / email address (not anonymized like Viva Insights PersonId) |
| Key challenge | The AuditData column contains nested JSON that varies by event type |
Working with AuditData
The AuditData column is a JSON string with event-specific details. Inspect its structure for each Operation and Workload before extracting fields.
Best practices:
- Explore before you extract. Inspect key frequency across a sample before writing extraction logic.
- Flatten one level only. Extract top-level and first-level nested fields; store deeper nesting as JSON strings.
- Wrap every parse in error handling. Some rows may contain truncated or malformed JSON.
- Filter by Workload first, then Operation. This is more efficient than scanning all rows.
Data quality considerations
- Malformed JSON: Wrap JSON parsing in error handling so one bad row does not crash the pipeline.
- Mixed schemas: Different
Operationtypes produce completely differentAuditDatastructures. - Encoding: Purview CSV exports frequently use UTF-8 BOM encoding (
utf-8-sig). If the first column name appears garbled, re-read with the correct encoding. - Volume: Audit logs can be extremely large. For exports exceeding 500MB, use chunked reading.
- User identifiers:
UserIdis typically a UPN (email address), not the anonymizedPersonIdused in Viva Insights. See Join Patterns for how to handle this.
Joining Viva Insights data sources
This section describes how to join different Viva Insights data sources together. Joining data correctly is essential but introduces pitfalls around key matching, time alignment, and granularity differences.
Overview of join scenarios
For Person, Consumption, GitHub and Agent exports, follow Joining Viva Insights queries. The scenarios below cover other sources, including Purview and external HR data.
| Join | Left Source | Right Source | Join Keys | Difficulty |
|---|---|---|---|---|
| Person query across time | Person query (period A) | Person query (period B) | PersonId |
Low |
| Person query ↔ Purview | Person query | Purview audit logs | PersonId ↔ UserId |
High |
| Person query ↔ HR data | Person query | External HRIS export | PersonId ↔ EmployeeId |
Medium |
| Person query ↔ License data | Person query | License assignment | PersonId ↔ UserId/UPN |
Medium |
Person query across time periods
Stack (concatenate) exports vertically using PersonId as the key. Check for duplicate (PersonId, MetricDate) pairs after stacking, especially if exports have overlapping date ranges.
Watch for: Column name mismatches between exports from different time periods (new metrics may have been added). Use bind_rows() (R) or pd.concat() (Python). Both handle mismatched columns by inserting NA/NaN.
Person query ↔ Purview audit data
The key challenge is that person query data uses an anonymized PersonId while Purview uses UserId (UPN/email). These are not the same identifier.
Mapping options:
- Authorised mapping table: use a verified
PersonId→ UPN lookup where available. Apply only contract-approved normalisation to UPNs and preserve opaque PersonIds exactly. - No authorised identity bridge: stop the person-level join; shared HR attributes are not unique identities and must not be used for fuzzy employee matching.
Time alignment: Purview logs are event-level. For a weekly Person Query, aggregate audit events to its detected week start before joining. Do not assume Monday or Sunday.
External HR data and license data
Both follow the same pattern: you need a mapping table to link the anonymized PersonId to the external system’s identifier. Key considerations:
- Point-in-time HR data: If your HRIS export is a snapshot, it won’t reflect historical role changes. Use versioned exports with effective dates where possible.
- License assignment timing: Small mismatches between license assignment dates and Copilot metric presence are expected (activation delays, mid-week changes). Large mismatches indicate mapping issues.
Checklist for joins
- Identify join keys and apply only contract-approved normalisation; preserve opaque IDs exactly
- Verify cardinality: is the join 1:1, 1:many, or many:many?
- Align time granularity: aggregate event-level data to the target period before joining
- Check join match rate: low match rates indicate key problems
- Verify row count: output should not have significantly more rows than the left table (for left joins)
Common analysis pitfalls
These are the most common analytical mistakes when working with Viva Insights exports. Each is subtle and can invalidate results silently.
1. Panel structure misunderstanding
Treating rows as independent observations when they are a person-time panel. This leads to overstated sample sizes, invalid statistical tests, and incorrect averages. Always aggregate to the person level first, then summarize across persons.
2. Missing value confusion (NA ≠ zero)
Replacing null with 0 conflates unknown coverage, ineligibility and observed inactivity. Keep those states separate, use explicit licence evidence, and omit adoption rates when the denominator is unresolved. See Licensed vs. unlicensed vs. active.
3. Survivorship bias
Assuming the same people appear in every time period. People leave, join, or go on leave during the analysis window. Check panel completeness and decide whether to use a balanced panel (same people throughout) or unbalanced panel (all data, accounting for entry/exit).
4. Small group suppression
Reporting metrics for groups with very few people, violating privacy thresholds. Viva Insights applies minimum group size thresholds (typically 5–10 users). Apply the same thresholds in custom analytics to protect individual privacy and ensure statistical reliability.
5. Time zone effects
Ignoring time zone differences when comparing collaboration metrics across geographies. After-hours metrics are relative to each person’s configured working hours. Purview timestamps are UTC while person query periods may use the tenant’s time zone. Document time zone assumptions in your output.
6. HR attribute changes over time
Assuming a person’s organisational attributes are static. People change roles, teams and levels over time. Each row reflects their attributes at that MetricDate. Decide whether to use point-in-time attribution (each row’s attributes as-is) or fixed-period attribution (attributes from a specific date).
7. Metric definition changes
Assuming metric definitions are consistent across all time periods. Microsoft periodically updates how metrics are calculated. Check for columns that appear only in a subset of periods, and note any sudden uniform shifts in metric distributions that may reflect methodology changes rather than behavior changes.
8. Double counting in aggregation
Aggregating metrics without respecting the panel structure. Common errors: summing hours across all rows (double-counts people across weeks), counting rows instead of unique persons, computing averages across all rows without per-person aggregation first.
9. Column name variations
Hard-coding column names from one tenant’s export. The same metric may appear under different names due to tenant configuration or localization. Use import_query() for name cleaning and extract_hr() for attribute discovery instead of hard-coding names.