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¶
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¶
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¶
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¶
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¶
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¶
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:
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¶
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 |