Skip to content

VCRI API

The Verified Carbon Reduction Infrastructure (VCRI) API provides endpoints for managing carbon reduction projects, setting verified baselines, recording periodic emissions measurements, and issuing tamper-evident verification certificates.

VCRI is the Nigeria MVP feature set, designed for SMEs undertaking decarbonization interventions with blockchain-ready certificate anchoring.


Architecture Overview

sequenceDiagram
    participant Client
    participant API as VCRI API
    participant Service as VcriService
    participant DB as PostgreSQL
    participant Anchor as AnchorService
    participant HCS as Hedera HCS

    Client->>API: POST /vcri/projects
    API->>Service: create_vcri_project()
    Service->>DB: INSERT vcri_projects
    DB-->>Service: VcriProject
    Service-->>API: VcriProjectResponse
    API-->>Client: 201 Created

    Client->>API: POST /vcri/projects/{id}/baseline
    API->>Service: upsert_vcri_baseline()
    Service->>DB: UPDATE vcri_projects (baseline fields)
    DB-->>Service: VcriProject (status=baseline_set)
    Service-->>API: VcriProjectResponse
    API-->>Client: 200 OK

    Client->>API: POST /vcri/projects/{id}/measurements
    API->>Service: create_vcri_measurement()
    Service->>DB: INSERT vcri_measurements
    DB-->>Service: VcriMeasurement
    Service-->>API: VcriMeasurementResponse
    API-->>Client: 201 Created

    Client->>API: POST /vcri/projects/{id}/certificates
    API->>Service: issue_vcri_certificate()
    Service->>DB: Compute reduction, SHA-256 hash
    Service->>DB: INSERT vcri_certificates
    Service->>Anchor: anchor_vcri_certificate_hash()
    Anchor->>HCS: TopicMessageSubmitTransaction
    HCS-->>Anchor: tx_id
    Anchor-->>Service: AnchorResult(submitted)
    Service->>DB: UPDATE anchor_tx_id, anchor_network
    Service-->>API: VcriCertificateResponse
    API-->>Client: 201 Created

Base URL

All VCRI endpoints are prefixed with /api/v1/vcri. All endpoints require JWT authentication.


Endpoints

List VCRI Projects

GET /api/v1/vcri/projects

Returns a paginated list of VCRI projects scoped to the current tenant.

Query Parameters:

Parameter Type Default Description
page integer 1 Page number (minimum 1)
page_size integer 20 Items per page (1-100)
company_id UUID Filter by company

Response: 200 OK

{
  "items": [
    {
      "id": "a1b2c3d4-...",
      "tenant_id": "t1e2n3...",
      "company_id": "c1o2m3...",
      "name": "Lagos Solar Panel Installation",
      "description": "Rooftop solar installation for manufacturing facility",
      "methodology": "VCRI-NG-EE-001",
      "status": "baseline_set",
      "baseline": {
        "year": 2024,
        "scope1_tco2e": 150.0,
        "scope2_tco2e": 320.5,
        "scope3_tco2e": null,
        "evidence": {},
        "total_tco2e": 470.5
      },
      "created_at": "2025-06-01T10:00:00Z",
      "updated_at": "2025-06-15T14:30:00Z"
    }
  ],
  "total": 12,
  "page": 1,
  "page_size": 20,
  "pages": 1
}

Create VCRI Project

POST /api/v1/vcri/projects

Creates a new VCRI project linked to a company. The project starts in draft status.

Request Body:

Field Type Required Description
company_id UUID Yes The company undertaking the carbon reduction
name string Yes Project name (1-255 characters)
description string No Detailed description of the intervention
methodology string No Methodology identifier (default: VCRI-NG-EE-001)

Example Request:

{
  "company_id": "c1o2m3p4-...",
  "name": "Generator Replacement Programme",
  "description": "Replace diesel generators with grid + solar hybrid",
  "methodology": "VCRI-NG-EE-001"
}

Response: 201 Created — Returns VcriProjectResponse

Errors:

Status Description
404 Not Found Company not found or not in tenant

Get VCRI Project

GET /api/v1/vcri/projects/{project_id}

Returns a single VCRI project by ID, including its baseline if set.

Path Parameters:

Parameter Type Description
project_id UUID The VCRI project ID

Response: 200 OK — Returns VcriProjectResponse

Errors:

Status Description
404 Not Found Project not found or not in tenant

Set/Update Baseline

POST /api/v1/vcri/projects/{project_id}/baseline

Sets or updates the verified emissions baseline for a project. This transitions the project status to baseline_set.

Path Parameters:

Parameter Type Description
project_id UUID The VCRI project ID

Request Body:

Field Type Required Description
year integer Yes Baseline year (1990-2100)
scope1_tco2e float Yes Scope 1 emissions in tCO2e (>= 0)
scope2_tco2e float Yes Scope 2 emissions in tCO2e (>= 0)
scope3_tco2e float No Scope 3 emissions in tCO2e (>= 0)
evidence object No Supporting evidence metadata

Example Request:

{
  "year": 2024,
  "scope1_tco2e": 150.0,
  "scope2_tco2e": 320.5,
  "scope3_tco2e": null,
  "evidence": {
    "audit_firm": "Carbon Verify NG",
    "report_date": "2024-12-01"
  }
}

Response: 200 OK — Returns updated VcriProjectResponse with baseline populated.

Errors:

Status Description
404 Not Found Project not found or not in tenant

Record Measurement

POST /api/v1/vcri/projects/{project_id}/measurements

Records a periodic emissions measurement for a project. Measurements represent actual emissions during a specific time period, compared against the baseline to calculate reductions.

Path Parameters:

Parameter Type Description
project_id UUID The VCRI project ID

Request Body:

Field Type Required Description
period_start date Yes Start of measurement period
period_end date Yes End of measurement period
scope1_tco2e float Yes Scope 1 emissions in tCO2e (>= 0)
scope2_tco2e float Yes Scope 2 emissions in tCO2e (>= 0)
scope3_tco2e float No Scope 3 emissions in tCO2e (>= 0)
evidence object No Supporting evidence metadata

Example Request:

{
  "period_start": "2025-01-01",
  "period_end": "2025-06-30",
  "scope1_tco2e": 90.0,
  "scope2_tco2e": 200.0,
  "scope3_tco2e": null,
  "evidence": {
    "meter_readings": true,
    "utility_bills": ["jan-2025.pdf", "feb-2025.pdf"]
  }
}

Response: 201 Created

{
  "id": "m1e2a3s4-...",
  "tenant_id": "t1e2n3...",
  "project_id": "p1r2o3...",
  "period_start": "2025-01-01",
  "period_end": "2025-06-30",
  "scope1_tco2e": 90.0,
  "scope2_tco2e": 200.0,
  "scope3_tco2e": null,
  "evidence": { "meter_readings": true },
  "total_tco2e": 290.0,
  "created_at": "2025-07-01T10:00:00Z"
}

Errors:

Status Description
404 Not Found Project not found or not in tenant

Issue Certificate

POST /api/v1/vcri/projects/{project_id}/certificates

Issues a verification certificate for a measurement. The certificate includes a deterministic SHA-256 hash of the reduction data and is optionally anchored to the Hedera Consensus Service for tamper-evident verification.

Path Parameters:

Parameter Type Description
project_id UUID The VCRI project ID

Request Body:

Field Type Required Description
measurement_id UUID Yes The measurement to certify

Example Request:

{
  "measurement_id": "m1e2a3s4-..."
}

Response: 201 Created

{
  "id": "c1e2r3t4-...",
  "tenant_id": "t1e2n3...",
  "project_id": "p1r2o3...",
  "measurement_id": "m1e2a3s4-...",
  "reduction_tco2e": 180.5,
  "certificate_hash": "a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1",
  "verified_at": "2025-07-01T12:00:00Z",
  "created_at": "2025-07-01T12:00:00Z"
}

Certificate Hash Computation:

The certificate hash is a SHA-256 digest of a canonical JSON payload containing:

{
  "vcri_version": "mvp-1",
  "project_id": "...",
  "company_id": "...",
  "methodology": "VCRI-NG-EE-001",
  "baseline": {
    "year": 2024,
    "scope1_tco2e": 150.0,
    "scope2_tco2e": 320.5,
    "scope3_tco2e": null,
    "total_tco2e": 470.5
  },
  "measurement": {
    "id": "...",
    "period_start": "2025-01-01",
    "period_end": "2025-06-30",
    "scope1_tco2e": 90.0,
    "scope2_tco2e": 200.0,
    "scope3_tco2e": null,
    "total_tco2e": 290.0
  },
  "reduction_tco2e": 180.5,
  "verified_at": "2025-07-01T12:00:00+00:00"
}

The JSON is serialized with sort_keys=True and separators=(",", ":") to ensure deterministic hashing.

Validation Rules:

  • Baseline must be set before issuing a certificate
  • Measurement must show a positive reduction (measurement total < baseline total)
  • Each measurement can only have one certificate (idempotent -- returns existing if already issued)
  • Measurement must belong to the specified project

Errors:

Status Description
400 Bad Request Baseline not set, or no reduction achieved
404 Not Found Project or measurement not found

Data Models

VcriProject Status Lifecycle

stateDiagram-v2
    [*] --> draft: Create Project
    draft --> baseline_set: Set Baseline
    baseline_set --> baseline_set: Update Baseline
    baseline_set --> active: First Measurement
    active --> verified: Certificate Issued
    verified --> active: New Measurement Period

Database Schema

erDiagram
    COMPANIES ||--o{ VCRI_PROJECTS : "has"
    VCRI_PROJECTS ||--o{ VCRI_MEASUREMENTS : "records"
    VCRI_PROJECTS ||--o{ VCRI_CERTIFICATES : "issues"
    VCRI_MEASUREMENTS ||--o| VCRI_CERTIFICATES : "certifies"

    VCRI_PROJECTS {
        uuid id PK
        uuid tenant_id FK
        uuid company_id FK
        string name
        text description
        string methodology
        string status
        int baseline_year
        numeric baseline_scope1_tco2e
        numeric baseline_scope2_tco2e
        numeric baseline_scope3_tco2e
        jsonb baseline_evidence
        timestamp baseline_set_at
        timestamp created_at
        timestamp updated_at
    }

    VCRI_MEASUREMENTS {
        uuid id PK
        uuid tenant_id FK
        uuid project_id FK
        date period_start
        date period_end
        numeric scope1_tco2e
        numeric scope2_tco2e
        numeric scope3_tco2e
        jsonb evidence
        timestamp created_at
    }

    VCRI_CERTIFICATES {
        uuid id PK
        uuid tenant_id FK
        uuid project_id FK
        uuid measurement_id FK "UNIQUE"
        numeric reduction_tco2e
        string certificate_hash "SHA-256, indexed"
        jsonb payload
        timestamp verified_at
        string anchor_network
        string anchor_tx_id
        timestamp anchor_timestamp
        timestamp created_at
    }

Blockchain Anchoring

Certificates are optionally anchored to the Hedera Consensus Service (HCS) for tamper-evident verification. Anchoring is best-effort: if it fails, the off-chain certificate is still issued.

Anchor Flow

flowchart LR
    C[Certificate Issued] --> P{Provider?}
    P -->|hedera_hcs| H[Build HCS Message]
    P -->|none/off| S[Skip Anchoring]
    H --> T[Submit to Hedera Topic]
    T -->|Success| U[Store tx_id + network]
    T -->|Failure| L[Log Warning]
    S --> D[Certificate Complete]
    U --> D
    L --> D

HCS Message Schema

{
  "schema": "vcri.certificate_hash.v1",
  "certificate_hash": "a3f8b2c1...",
  "project_id": "...",
  "company_id": "...",
  "methodology": "VCRI-NG-EE-001",
  "verified_at": "2025-07-01T12:00:00+00:00"
}

Only the hash and metadata are anchored -- no raw emissions data is sent on-chain (privacy-preserving design).

Configuration

Environment Variable Description Default
VCRI_ANCHOR_PROVIDER Anchor provider (hedera_hcs, none) none
HEDERA_ACCOUNT_ID Hedera account ID (e.g., 0.0.12345)
HEDERA_PRIVATE_KEY Hedera private key
HEDERA_TOPIC_ID HCS topic ID for VCRI certificates
HEDERA_NETWORK Network (testnet or mainnet) testnet

Pydantic Schemas

VcriProjectCreate

class VcriProjectCreate(BaseModel):
    company_id: UUID
    name: str          # min 1, max 255 characters
    description: str | None = None
    methodology: str = "VCRI-NG-EE-001"  # max 100 characters

VcriBaselineUpsert

class VcriBaselineUpsert(BaseModel):
    year: int          # 1990-2100
    scope1_tco2e: float  # >= 0
    scope2_tco2e: float  # >= 0
    scope3_tco2e: float | None = None  # >= 0 if provided
    evidence: dict[str, Any] = {}

VcriMeasurementCreate

class VcriMeasurementCreate(BaseModel):
    period_start: date
    period_end: date
    scope1_tco2e: float  # >= 0
    scope2_tco2e: float  # >= 0
    scope3_tco2e: float | None = None  # >= 0 if provided
    evidence: dict[str, Any] = {}

VcriCertificateCreate

class VcriCertificateCreate(BaseModel):
    measurement_id: UUID

Error Codes

HTTP Status Error Context
400 Baseline not set Attempting to issue certificate without baseline
400 No emissions reduction achieved Measurement total >= baseline total
404 Company not found Company ID not in tenant
404 VCRI project not found Project ID not in tenant
404 Measurement not found Measurement not in project or tenant

Source Files

File Description
backend/app/api/v1/vcri.py API route handlers
backend/app/services/vcri_service.py Business logic and certificate issuance
backend/app/services/vcri_anchor_service.py Hedera HCS blockchain anchoring
backend/app/models/database/vcri_project.py SQLAlchemy project model
backend/app/models/database/vcri_measurement.py SQLAlchemy measurement model
backend/app/models/database/vcri_certificate.py SQLAlchemy certificate model
backend/app/models/schemas/vcri.py Pydantic request/response schemas
frontend/src/app/dashboard/vcri/page.tsx VCRI dashboard UI
frontend/src/lib/api/hooks/useVcri.ts React Query hooks for VCRI