Technical

Building an engineering Scope 3 data pipeline from scratch

A

Anand Srinivasan

17 Jul 2026 · 7 min read

For a software company, Scope 3 is the emissions story. No factory, no fleet, Scope 1 and 2 are rounding errors. Category 1 (cloud, CI/CD, SaaS tools) and Category 11 (employee hardware) are where the numbers actually live.

This is the system we built for EmitCI's Engineering Scope 3 feature. Every architectural decision below is live in the product.

What EmitCI tracks

EmitCI's /scope3 page covers five layers: cloud compute (EC2, Cloud Run, Azure VMs, RDS, S3), CI/CD pipeline runners, SaaS tools via spend-based estimation, employee hardware embodied carbon, and network/storage. Together these cover GHG Protocol Cat 1 and Cat 11.

SaaS: DEFRA's 0.14 kgCO2e/USD spend factor converts monthly invoices to carbon with no vendor telemetry needed. Slack, GitHub, Jira, Figma, Datadog and others are preset. Add any tool with a monthly spend figure and it appears in the summary immediately.

Cat 11 is embodied hardware carbon. A MacBook Pro M3 carries ~147 kgCO2e from manufacturing before it is powered on. At 50 engineers on a 3-year refresh cycle: 2,450 kgCO2/year from laptops alone, often more than total cloud compute. Most carbon dashboards count neither category. EmitCI counts both.

The data model

One time-series store: one row per billing record (service × region × period), holding both cost and carbon on the same record. Cost and carbon share a schema, not separate systems. Every record tracks which data source produced its carbon figure — the foundation for confidence scoring — plus the cost-allocation tags for attribution.

Three supporting stores: SaaS tool emissions (tool + monthly spend + estimated kgCO2/month), hardware declarations (headcount + device type + refresh cycle), and locked base year periods on the organisation record.

Multi-account is built in from the start. Multiple AWS accounts or GCP projects sync into the same carbon store and aggregate on the /scope3 page.

The ingestion pipeline

EmitCI's sync pipeline runs in four stages: fetch, calculate, merge, store.

┌──────────────────────────────────────────────────────┐
│                      INGESTION                        │
│  AWS Cost Explorer · GCP Billing · Azure Cost Mgmt    │
│  pulls usage by service, region, and billing period   │
│  extracts cost-allocation tags: Team, Env, Project    │
└────────────────────────┬─────────────────────────────┘
                         │
                         ▼
┌──────────────────────────────────────────────────────┐
│                     ENRICHMENT                        │
│  Energy  = usage_hours × (watts / 1000)               │
│  CO₂     = energy × regional grid intensity (IEA)     │
│  Accuracy = ±30-50%                                   │
│  If provider-native carbon API available:             │
│    CO₂   = native MTCO₂e (measured)  ±10%            │
└────────────────────────┬─────────────────────────────┘
                         │
                         ▼
┌──────────────────────────────────────────────────────┐
│               UNIFIED CARBON STORE                    │
│  cost · energy · co2 · tags · data source per row    │
│  + SaaS tool emissions  (spend × DEFRA factor)       │
│  + Hardware emissions   (headcount × embodied CO₂)   │
└───────────────┬──────────────────────────────────────┘
                │
     ┌──────────┴──────────────┐
     ▼                         ▼
┌──────────────────┐   ┌───────────────────────┐
│ Cost & Carbon    │   │   Compliance Exports   │
│ Dashboards       │   │   ESRS E1 CSV          │
│ Tag attribution  │   │   CDP C6.5 Excel       │
│ Anomaly alerts   │   │   Methodology PDF      │
└──────────────────┘   └───────────────────────┘

Fetch calls AWS Cost Explorer, GCP Billing API, and Azure Cost Management. Usage is pulled grouped by service and region. Cost-allocation tags (Team, Environment, Project) are extracted here and stored on every carbon row so the same filter FinOps uses for cost attribution works for carbon attribution with zero extra setup.

Calculate applies two public lookups: instance TDP in watts from manufacturer specs, and IEA regional grid intensity in gCO2/kWh. Energy = usage_hours × (watts ÷ 1000). CO2 = energy × intensity. This is the ±30-50% path.

Merge is where accuracy improves. AWS's Sustainability API (which replaced the deprecated CCFT in June 2026) returns measured MTCO2e per service and region. When available, it replaces the energy × intensity number and accuracy goes from ±30-50% to ±10%. For GCP, customers who have the BigQuery Carbon Footprint export enabled can supply their dataset ID and get the same improvement. Azure continues on energy × intensity only — the Microsoft EMIS API requires separate portal registration and is being retired March 2027.

Store writes one enriched record per billing row holding both cost and carbon. Every cost query becomes a carbon query.

Tag attribution

Cost-allocation tags from your cloud provider (Team, Environment, Project) are stored alongside each carbon record. The /scope3 page exposes a tag filter dropdown — selecting a team instantly scopes the entire summary to that team's emissions. If 40% of spend has no Team tag, 40% of carbon is unattributed. EmitCI surfaces that coverage gap explicitly, auditors expect to see it quantified.

Confidence scoring

CSRD requires disclosure of which emission factors were used, their source, and the uncertainty range. Every record in EmitCI's carbon store tracks its data source. The summary view computes a CO2-weighted blended confidence across all sources for the installation.

Data Source                        Accuracy     Confidence
───────────────────────────────────────────────────────────
Provider-native API (AWS, GCP)     ±10%         ████████  90%
IEA regional annual averages       ±30–50%      █████     55%
IEA global average fallback        ±50%+        ████      40%
DEFRA spend-based (SaaS tools)     ±40%         █████     60%
───────────────────────────────────────────────────────────
Blended score = CO₂-weighted average across all sources
Target ≥ 70% measured for a defensible CSRD disclosure

A blended score of 70%+ is defensible for a first filing. The /scope3 summary strip shows the blended score alongside total CO2. A disclosure with no confidence metadata is incomplete.

The base year trap

CSRD requires a base year for every material Scope 3 category. EmitCI's default retention window is 365 days for free/starter plans, 1,095 days for Enterprise. Start measuring in 2025, retention runs Jan 2026 — FY2024 data is gone, no YoY comparison, filing fails.

  Q1 2025           Jan 2026            Jul 2026
     │                  │                   │
     ▼                  ▼                   ▼
  Start             Retention           CSRD filing
  measuring    →    deletes        →    base year
  emissions         FY2024 data         MISSING
                         ↑
                  ← THE TRAP ─────────────────────
                    Base year gone before filing.
                    YoY comparison impossible.
  ─────────────────────────────────────────────────
  Solution: lock periods BEFORE retention runs
    Mark FY2024, FY2025 as closed
    → Retention skips locked years permanently
    → Base year preserved for audit, forever

EmitCI solves this with period locking. The Base Year card on the /scope3 page lets you set a base year and close prior periods. Locked years are permanently exempted from the retention sweep, preserved for audit regardless of plan limits. If you have 2024 data in EmitCI today, lock it now. It takes seconds.

The compliance export chain

Three documents, all generated from the same underlying data store. Available on Team and Enterprise plans from the /scope3 page.

  Cloud emissions + SaaS tool emissions + Hardware emissions
                              │
            ┌─────────────────┼─────────────────┐
            ▼                 ▼                 ▼
     ESRS E1 CSV        CDP C6.5 xlsx     Methodology PDF
     ─────────────      ─────────────     ───────────────
     One row per        One row per       Org boundary
     emission source    Scope 3 cat.      Data sources
     Includes rows      MTCO₂e values     Emission factors
     for EXCLUDED  ←    Eval. status      Uncertainty range
     categories         % measured        Exclusion reasons
     ↑                  Completeness
     Auditors check     statement sheet   Required for any
     this first                           third-party audit

ESRS E1 CSV: one row per emission source, plus explicit exclusion rows for every Scope 3 category not covered with a stated reason. EmitCI generates these automatically. Most first-time filings are flagged here, auditors check completeness before they check the numbers.

CDP C6.5 workbook: one row per Scope 3 category in MTCO2e (grams ÷ 1,000,000,000), with a completeness statement sheet. EmitCI outputs this as a ready-to-upload Excel file.

Methodology PDF: org boundary, data sources, emission factors, uncertainty ranges, exclusion list. Generated from the installation's actual data. Auditors read this first. Without it, the numbers are unverifiable and the filing is non-compliant.

What the numbers look like

Layer               Share
──────────────────────────────────────────
Cloud compute        56%
Databases            15%
SaaS tools           11%
CI/CD runners         9%
Hardware              6%
Storage               3%
──────────────────────────────────────────
50-engineer team · ~$30k/month cloud spend
Hardware + SaaS = ~17% combined
Both required by GHG Protocol
Most carbon tools count neither

What you get with EmitCI

① Connect cloud accounts   AWS / GCP / Azure (multi-account supported)
        │                  Billing API fetch starts immediately
        ▼
② Connect GitHub           CI/CD runner carbon tracked per workflow run
        │
        ▼
③ Declare SaaS + hardware  /scope3 → SaaS Tools card + Hardware card
        │                  spend × DEFRA factor · headcount × embodied CO₂
        ▼
④ Lock base year           /scope3 → Base Year card
        │                  Closed periods exempted from retention forever
        ▼
⑤ Download compliance      /scope3 → Compliance Downloads (Team+)
                           ESRS E1 CSV · CDP C6.5 xlsx · Methodology PDF

The exports fall out of the data model naturally — no separate build required. The only step that has a deadline is locking the base year. Everything else can be added incrementally.

Get started free

Start measuring your CI carbon today

Connect your GitHub account in 2 minutes. Per-workflow carbon and cost estimates appear within 24 hours. No code changes required.

Install GitHub App — free for 14 days