PPA Upload API Documentation
Overview
The PPA (Power Purchase Agreement) Upload API allows you to upload energy purchase and sales agreements with detailed pricing structures. You can upload offers for both Upstream (selling energy) and Downstream (buying energy) contracts.
Endpoint: POST /tariff-management/ppa/upload
Understanding the Structure
Before uploading PPA offers, it's crucial to understand how contracts are organized:
┌─────────────────────────────────────────────────────────────┐
│ OFFER: "Solar PPA Upstream" │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ CONFIGURATION │ │
│ │ - Technology: Solar │ │
│ │ - PPA Structure: PayAsForecasted │ │
│ │ - Guarantee of Origin: Provider │ │
│ │ - Negative Prices: Included │ │
│ │ - Hedge Share: 75% │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ PRICE MATRIX (Array of Contracts) │ │
│ │ │ │
│ │ Yearly Contracts (36 entries): │ │
│ │ Start 2026: 1Y, 2Y, 3Y, 4Y, 5Y, 6Y ─────┐ │ │
│ │ Start 2027: 1Y, 2Y, 3Y, 4Y, 5Y, 6Y ─────┤ │ │
│ │ Start 2028: 1Y, 2Y, 3Y, 4Y, 5Y, 6Y ─────┤ │ │
│ │ ... (2029, 2030, 2031) ─────┤ │ │
│ │ │ │ │
│ │ Quarterly Contracts (32 entries): │ │ │
│ │ Start 2026Q1: 1Q, 2Q, 3Q, 4Q ───────────┤ │ │
│ │ Start 2026Q2: 1Q, 2Q, 3Q, 4Q ───────────┤ │ │
│ │ ... (2026Q3, Q4, 2027Q1-Q4) ───────────┘ │ │
│ │ │ │
│ │ ⚠️ All different tenors (quarterly AND yearly) │ │
│ │ must be in THIS array - 68+ entries total │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Key Concepts
IMPORTANT: All contract tenors and start dates for a PPA configuration must be within the same offer's priceMatrix array. This means you can have 1 entry or 100+ entries in a single priceMatrix, mixing quarterly contracts (1Q-4Q) and yearly contracts (1Y-12Y).
What this means:
- ✅ One configuration = One priceMatrix array containing ALL contracts
- ✅ You can mix quarterly and yearly tenors in the same priceMatrix
- ✅ Real-world examples show 68+ priceMatrix entries in a single offer
- ❌ You cannot upload the same start + tenor combination twice (validation will fail)
- ❌ You cannot split tenors across multiple offers with the same configuration
Quick Start
- Create a JSON file with your PPA offer data
- Choose tariff type:
5for Upstream (selling) or6for Downstream (buying) - Upload the file using the API endpoint
- Receive confirmation with offer IDs
What is a PPA Offer?
A PPA (Power Purchase Agreement) offer defines:
- Energy direction (Upstream = selling, Downstream = buying)
- Technology type (Solar, Wind, or Biomass)
- Pricing structure (quarterly or yearly periods)
- Contract terms (hedge percentage, payment structure)
- Guarantee of Origin handling
- Fees associated with the agreement
Critical Concept: A PPA offer defines a pricing configuration, and all possible contract start dates and durations for that configuration must be included in a single priceMatrix array. This array can contain 1 entry or 100+ entries, mixing quarterly contracts (1Q-4Q) and yearly contracts (1Y-12Y).
JSON File Structure
Basic Structure
{
"offers": [
{
// Offer details
}
]
}
Field Reference
Offer Object
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Descriptive name for the PPA offer |
tariffType |
integer | Yes | 5 for Upstream (selling), 6 for Downstream (buying) |
countryCode |
string | Yes | ISO country code (e.g., "DE") |
description |
string | No | Optional description |
configuration |
object | Yes | Technical configuration |
fees |
object | Yes | Fee structure |
priceMatrix |
array | Yes | Contract periods with pricing |
Configuration Object
| Field | Type | Required | Options | Description |
|---|---|---|---|---|
technology |
string | Yes | "Solar", "Wind", "Biomass" |
Energy source type |
ppaStructure |
string | Yes | "PayAsForecasted", "PayAsProduced" |
Payment calculation method |
guaranteeOfOrigin |
string | Yes | "None", "Provider", "Customer" |
Who handles GOO certificates |
negativePrices |
string | Yes | "Included", "Excluded" |
Treatment of negative price hours |
hedgeSharePercent |
decimal | No | 0-100 | Percentage of production hedged (optional) |
Configuration Examples:
Pay As Forecasted - Payment based on forecasted production:
"configuration": {
"technology": "Solar",
"ppaStructure": "PayAsForecasted",
"guaranteeOfOrigin": "Provider",
"negativePrices": "Included",
"hedgeSharePercent": 75
}
Pay As Produced - Payment based on actual production:
"configuration": {
"technology": "Wind",
"ppaStructure": "PayAsProduced",
"guaranteeOfOrigin": "Customer",
"negativePrices": "Excluded",
"hedgeSharePercent": 80
}
Fees Object
| Field | Type | Required | Description |
|---|---|---|---|
guaranteeOfOriginFeeEurPerMWh |
decimal | No | Default GOO fee per MWh (can be overridden per period) |
basicFeePerYear |
decimal | No | Annual basic fee in EUR |
Price Matrix Structure
IMPORTANT: All tenors for a PPA configuration must be defined in the same offer's priceMatrix array, even if they have different runtimes (Quarter, Year). You can have 1Q, 4Q, 1Y, and 12Y all in the same priceMatrix array.
The priceMatrix is an array of contract objects:
"priceMatrix": [
{
"start": "2026Q2", // Start period
"tenor": "3Q", // Contract duration
"prices": {
"2026Q2": { "priceEurPerMWh": 64.0 },
"2026Q3": { "priceEurPerMWh": 65.0 },
"2026Q4": { "priceEurPerMWh": 66.0 }
}
}
]
Contract Object
| Field | Type | Required | Description |
|---|---|---|---|
start |
string | Yes | Start period (quarterly: "2026Q2", yearly: "2027") |
tenor |
string | Yes | Contract duration (e.g., "1Q" for 1 quarter, "4Q" for 4 quarters, "1Y" for 1 year, "6Y" for 6 years) |
prices |
object | Yes | Period-based pricing dictionary |
Understanding Start Periods and Tenors
Quarterly Start Periods: Format YYYYQN where N is 1-4
2026Q1= Q1 2026 (January-March)2026Q2= Q2 2026 (April-June)2026Q3= Q3 2026 (July-September)2026Q4= Q4 2026 (October-December)
Yearly Start Periods: Format YYYY
2026= Year 20262027= Year 20272028= Year 2028
Quarterly Tenors: Format NQ where N is duration in quarters
1Q= 1 quarter (3 months)2Q= 2 quarters (6 months)3Q= 3 quarters (9 months)4Q= 4 quarters (1 year)- Can go up to
48Q(12 years) or more
Yearly Tenors: Format NY where N is duration in years
1Y= 1 year2Y= 2 years3Y= 3 years6Y= 6 years- Can go up to
12Yor more
Price Key Format Requirements
CRITICAL: Price keys in the
pricesobject must match the tenor format:
| Tenor Format | Required Price Key Format | Example |
|---|---|---|
| Quarterly (1Q, 4Q) | Quarterly ("YYYYQN") | "2026Q1", "2026Q2", "2026Q3" |
| Yearly (1Y, 3Y) | Yearly ("YYYY") | "2026", "2027", "2028" |
Examples:
- ✅ Tenor
"4Q"starting"2026Q1"→ Price keys:"2026Q1","2026Q2","2026Q3","2026Q4" - ✅ Tenor
"3Y"starting"2026"→ Price keys:"2026","2027","2028" - ❌ Tenor
"1Y"with quarterly price keys"2026Q1","2026Q2", etc. → INVALID
Price Entry Fields
| Field | Type | Required | Description |
|---|---|---|---|
priceEurPerMWh |
decimal | Yes | PPA price in EUR per MWh for this period |
guaranteeOfOriginFeeEurPerMWh |
decimal | No | Override default GOO fee for this period |
Complete Examples
Example 1: Simplified Example - Basic Concept
This simplified example shows 4 different contracts under ONE configuration, demonstrating how quarterly and yearly tenors mix in a single priceMatrix:
{
"offers": [
{
"name": "Solar PPA - Basic Example",
"tariffType": 5,
"countryCode": "DE",
"configuration": {
"technology": "Solar",
"ppaStructure": "PayAsForecasted",
"guaranteeOfOrigin": "Provider",
"negativePrices": "Included",
"hedgeSharePercent": 75
},
"fees": {
"guaranteeOfOriginFeeEurPerMWh": 0.5,
"basicFeePerYear": 1200
},
"priceMatrix": [
{
"start": "2026Q1",
"tenor": "1Q",
"prices": {
"2026Q1": {
"priceEurPerMWh": 64.0,
"guaranteeOfOriginFeeEurPerMWh": 0.5
}
}
},
{
"start": "2026Q1",
"tenor": "4Q",
"prices": {
"2026Q1": { "priceEurPerMWh": 64.0, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q2": { "priceEurPerMWh": 65.0, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q3": { "priceEurPerMWh": 66.0, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q4": { "priceEurPerMWh": 67.0, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026",
"tenor": "1Y",
"prices": {
"2026": {
"priceEurPerMWh": 65.5,
"guaranteeOfOriginFeeEurPerMWh": 0.5
}
}
},
{
"start": "2026",
"tenor": "3Y",
"prices": {
"2026": { "priceEurPerMWh": 65.5, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2027": { "priceEurPerMWh": 66.0, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2028": { "priceEurPerMWh": 66.5, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
}
]
}
]
}
What this shows:
- ✅ 4 contracts under ONE configuration in ONE priceMatrix array
- ✅ Quarterly contracts: 1Q and 4Q starting from 2026Q1 (using quarterly price keys)
- ✅ Yearly contracts: 1Y and 3Y starting from 2026 (using yearly price keys)
- ✅ Different start periods mixed in the same array
- This demonstrates the key concept: All tenors belong in the same priceMatrix, even with different runtimes
Example 2: Real-World Upstream Example (Solar)
This example is based on a real production upload showing 68+ priceMatrix entries in a single offer. We show a representative subset to illustrate the pattern:
{
"offers": [
{
"name": "Solar PPA Upstream - Comprehensive Pricing",
"tariffType": 5,
"countryCode": "DE",
"description": "Solar PPA with extensive quarterly and yearly options",
"configuration": {
"technology": "Solar",
"ppaStructure": "PayAsForecasted",
"guaranteeOfOrigin": "Provider",
"negativePrices": "Included",
"hedgeSharePercent": 75
},
"fees": {
"guaranteeOfOriginFeeEurPerMWh": 0.5,
"basicFeePerYear": 1200
},
"priceMatrix": [
// Quarterly Contracts - 8 starts × 4 tenors = 32 entries
// 2026Q1 start with 4 tenor options
{
"start": "2026Q1",
"tenor": "1Q",
"prices": {
"2026Q1": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026Q1",
"tenor": "2Q",
"prices": {
"2026Q1": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q2": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026Q1",
"tenor": "3Q",
"prices": {
"2026Q1": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q2": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q3": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026Q1",
"tenor": "4Q",
"prices": {
"2026Q1": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q2": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q3": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2026Q4": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
// ... (2026Q2, 2026Q3, 2026Q4, 2027Q1, 2027Q2, 2027Q3, 2027Q4 - same pattern, 28 more entries)
// Yearly Contracts - 6 starts × 6 tenors = 36 entries
// 2026 start with 6 tenor options
{
"start": "2026",
"tenor": "1Y",
"prices": {
"2026": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026",
"tenor": "2Y",
"prices": {
"2026": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2027": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026",
"tenor": "3Y",
"prices": {
"2026": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2027": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2028": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026",
"tenor": "4Y",
"prices": {
"2026": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2027": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2028": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2029": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026",
"tenor": "5Y",
"prices": {
"2026": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2027": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2028": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2029": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2030": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
},
{
"start": "2026",
"tenor": "6Y",
"prices": {
"2026": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2027": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2028": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2029": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2030": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 },
"2031": { "priceEurPerMWh": 64.33, "guaranteeOfOriginFeeEurPerMWh": 0.5 }
}
}
// ... (2027, 2028, 2029, 2030, 2031 - same pattern, 30 more entries)
// Total: 68+ priceMatrix entries in ONE offer
]
}
]
}
Real-World Pattern Explained:
- Quarterly Options: 8 quarterly starts (2026Q1-2027Q4) × 4 tenors (1Q-4Q) = 32 entries
- Yearly Options: 6 yearly starts (2026-2031) × 6 tenors (1Y-6Y) = 36 entries
- Total: 68+ priceMatrix entries in a single offer under ONE configuration
- All contracts (quarterly AND yearly) are in the same priceMatrix array
- This gives customers maximum flexibility to choose start date and contract duration
Example 3: Real-World Downstream Example (Biomass)
This example shows a Downstream (buying) PPA with Biomass technology. The structure is identical to Upstream, only the tariffType differs:
{
"offers": [
{
"name": "Biomass PPA Downstream - Comprehensive Pricing",
"tariffType": 6,
"countryCode": "DE",
"description": "Biomass PPA for energy purchase with flexible terms",
"configuration": {
"technology": "Biomass",
"ppaStructure": "PayAsProduced",
"guaranteeOfOrigin": "Customer",
"negativePrices": "Excluded",
"hedgeSharePercent": 80
},
"fees": {
"guaranteeOfOriginFeeEurPerMWh": 0.3,
"basicFeePerYear": 1500
},
"priceMatrix": [
// Same pattern as Upstream example
// Quarterly contracts: 32 entries
{
"start": "2026Q1",
"tenor": "1Q",
"prices": {
"2026Q1": { "priceEurPerMWh": 70.0, "guaranteeOfOriginFeeEurPerMWh": 0.3 }
}
},
// ... (more quarterly entries)
// Yearly contracts: 36 entries
{
"start": "2026",
"tenor": "1Y",
"prices": {
"2026": { "priceEurPerMWh": 70.0, "guaranteeOfOriginFeeEurPerMWh": 0.3 }
}
}
// ... (more yearly entries)
// Total: 68+ entries
]
}
]
}
Key Differences from Upstream:
- ✅
tariffType: 6(Downstream - buying) instead of 5 (Upstream - selling) - ✅ Same structure, same pattern, same priceMatrix approach
- ✅ Technology changed to "Biomass"
- ✅ Customer handles GOO certificates instead of Provider
- ✅ Negative prices excluded
Example 4: Batch Upload (Multiple Offers)
You can upload multiple PPA offers (Upstream and Downstream) in one file:
{
"offers": [
{
"name": "Solar PPA Upstream",
"tariffType": 5,
"countryCode": "DE",
"configuration": {
"technology": "Solar",
"ppaStructure": "PayAsForecasted",
"guaranteeOfOrigin": "Provider",
"negativePrices": "Included",
"hedgeSharePercent": 75
},
"fees": {
"guaranteeOfOriginFeeEurPerMWh": 0.5,
"basicFeePerYear": 1200
},
"priceMatrix": [
// ... priceMatrix entries
]
},
{
"name": "Wind PPA Downstream",
"tariffType": 6,
"countryCode": "DE",
"configuration": {
"technology": "Wind",
"ppaStructure": "PayAsProduced",
"guaranteeOfOrigin": "Customer",
"negativePrices": "Excluded",
"hedgeSharePercent": 80
},
"fees": {
"guaranteeOfOriginFeeEurPerMWh": 0.3,
"basicFeePerYear": 1500
},
"priceMatrix": [
// ... priceMatrix entries
]
}
]
}
Common Patterns
Here are typical patterns for structuring your PPA priceMatrix:
Pattern 1: Comprehensive Quarterly Coverage
Scenario: Offer customers flexibility to start contracts in any quarter with various duration options.
Structure:
- 4 quarterly starts per year (Q1, Q2, Q3, Q4)
- Each start offers 4 tenor options (1Q, 2Q, 3Q, 4Q)
- For 2 years of coverage: 8 starts × 4 tenors = 32 priceMatrix entries
Example:
2026Q1 start → 1Q, 2Q, 3Q, 4Q tenors (4 contracts)
2026Q2 start → 1Q, 2Q, 3Q, 4Q tenors (4 contracts)
2026Q3 start → 1Q, 2Q, 3Q, 4Q tenors (4 contracts)
2026Q4 start → 1Q, 2Q, 3Q, 4Q tenors (4 contracts)
2027Q1 start → 1Q, 2Q, 3Q, 4Q tenors (4 contracts)
... (continues for all quarters)
= 32 total priceMatrix entries in ONE offer
Pattern 2: Long-term Yearly Contracts
Scenario: Multi-year contracts with different start dates, offering flexibility in contract length.
Structure:
- 6 yearly starts (2026-2031)
- Each start offers 6 tenor options (1Y, 2Y, 3Y, 4Y, 5Y, 6Y)
- Total: 6 starts × 6 tenors = 36 priceMatrix entries
Example:
2026 start → 1Y, 2Y, 3Y, 4Y, 5Y, 6Y tenors (6 contracts)
2027 start → 1Y, 2Y, 3Y, 4Y, 5Y, 6Y tenors (6 contracts)
2028 start → 1Y, 2Y, 3Y, 4Y, 5Y, 6Y tenors (6 contracts)
... (continues through 2031)
= 36 total priceMatrix entries in ONE offer
Pattern 3: Mixed Quarterly and Yearly (Maximum Flexibility)
Scenario: Combine short-term quarterly options with long-term yearly options for maximum customer choice.
Structure:
- Quarterly: 8 starts (2026Q1-2027Q4) × 4 tenors (1Q-4Q) = 32 entries
- Yearly: 6 starts (2026-2031) × 6 tenors (1Y-6Y) = 36 entries
- Total: 68 priceMatrix entries in ONE offer
Example:
Quarterly Coverage:
2026Q1 → 1Q, 2Q, 3Q, 4Q
2026Q2 → 1Q, 2Q, 3Q, 4Q
... (through 2027Q4)
Yearly Coverage:
2026 → 1Y, 2Y, 3Y, 4Y, 5Y, 6Y
2027 → 1Y, 2Y, 3Y, 4Y, 5Y, 6Y
... (through 2031)
= 68 total priceMatrix entries in ONE offer
Note: This is the pattern used in real production uploads, giving customers the most comprehensive set of contract options.
Pattern 4: Simple Fixed-Term Contract
Scenario: Single contract with fixed start and duration (simplest case).
Structure:
- 1 start period
- 1 tenor
- Total: 1 priceMatrix entry
Example:
"priceMatrix": [
{
"start": "2026Q1",
"tenor": "4Q",
"prices": {
"2026Q1": { "priceEurPerMWh": 64.0 },
"2026Q2": { "priceEurPerMWh": 65.0 },
"2026Q3": { "priceEurPerMWh": 66.0 },
"2026Q4": { "priceEurPerMWh": 67.0 }
}
}
]
How to Upload
Using cURL
curl -X POST \
https://api.trace.com/tariff-management/ppa/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@ppa_offers.json"
Using Postman
- Set request type to POST
- Enter URL:
https://api.trace.com/tariff-management/ppa/upload - Go to Headers tab, add:
Authorization: Bearer YOUR_TOKEN - Go to Body tab, select form-data
- Add key named
file, change type to File - Select your JSON file
- Click Send
Using PowerShell
$filePath = 'C:\path\to\ppa_offers.json'
$headers = @{ Authorization = 'Bearer YOUR_TOKEN' }
$form = @{ file = Get-Item $filePath }
Invoke-RestMethod -Uri 'https://api.trace.com/tariff-management/ppa/upload' `
-Method Post `
-Headers $headers `
-Form $form
Response Format
Success Response
{
"results": [
{
"offerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"success": true,
"message": "✓ PPA Offer 'Solar PPA Q2-Q4 2026' imported successfully with 3 price periods"
}
]
}
Error Response
{
"results": [
{
"offerId": null,
"success": false,
"message": "PPA Import validation failed:\nOffer 1 (Solar PPA): Hedge share percent must be between 0 and 100"
}
]
}
Validation Rules
Required Fields:
- All required fields must be present
offersarray must contain at least one offer- Each price period must have
priceEurPerMWh
Value Constraints:
tariffTypemust be5(Upstream) or6(Downstream)hedgeSharePercentmust be between 0 and 100- Quarterly periods: format
YYYYQN(N = 1-4) - Yearly periods: format
YYYY
Data Validation:
- All offers validated before import
- If any offer fails validation, entire upload is rejected
- Duplicate offers create new versions (history preserved)
Unique Tenor Combinations:
- You cannot upload the same start + tenor combination twice within the same offer
- Each priceMatrix entry must be unique
- ✅ Valid:
{"start": "2026Q1", "tenor": "1Q"}and{"start": "2026Q2", "tenor": "1Q"} - ✅ Valid:
{"start": "2026Q1", "tenor": "1Q"}and{"start": "2026Q1", "tenor": "4Q"} - ❌ Invalid: Two entries with
{"start": "2026Q1", "tenor": "1Q"}
Configuration Grouping:
- All tenors for a PPA configuration must be within the same offer's priceMatrix array
- You cannot split tenors across multiple offers with the same configuration
- ✅ Valid: One offer with 68+ priceMatrix entries covering all start dates and tenors
- ❌ Invalid: Multiple offers with same configuration, each with subset of tenors
Price Key Format Consistency:
- Quarterly tenors (e.g., "1Q", "4Q") MUST use quarterly price keys ("2026Q1", "2026Q2")
- Yearly tenors (e.g., "1Y", "6Y") MUST use yearly price keys ("2026", "2027")
- Each price period in the prices object must match the tenor format
- ✅ Valid: Tenor "4Q" with price keys "2026Q1", "2026Q2", "2026Q3", "2026Q4"
- ❌ Invalid: Tenor "1Y" with price keys "2026Q1", "2026Q2", "2026Q3", "2026Q4"
Tenor Duration Coverage:
- The number of price entries must match the tenor duration
- 1Q tenor → 1 price entry
- 4Q tenor → 4 price entries
- 1Y tenor → 1 price entry
- 3Y tenor → 3 price entries
- ✅ Valid: Tenor "3Y" starting "2026" with price keys "2026", "2027", "2028" (3 entries)
- ❌ Invalid: Tenor "3Y" starting "2026" with only price keys "2026", "2027" (2 entries - missing 2028)
Frequently Asked Questions (FAQ)
Q: Can I mix quarterly and yearly tenors in the same priceMatrix?
A: Yes! You can mix different time periods and contract durations within the same offer's priceMatrix array. A single offer can contain both quarterly contracts (1Q-4Q) and yearly contracts (1Y-12Y). This is demonstrated in the real-world examples with 68+ entries combining quarterly and yearly contracts.
Q: How many priceMatrix entries can one offer have?
A: There is no hard limit, but each entry must be unique (unique start + tenor combination). Real-world examples show 68+ entries in one offer mixing quarterly and yearly contracts. You could theoretically have even more if your business requires it.
Q: What happens if I upload the same tenor twice for the same start date?
A: The API will reject the upload with a validation error. Each start + tenor combination must be unique within an offer. For example, you cannot have two entries with {"start": "2026Q1", "tenor": "1Q"}.
Q: Can I use quarterly prices for a yearly tenor?
A: No. Price key format must match the tenor format:
- Yearly tenors (1Y, 3Y, etc.) MUST use yearly price keys ("2026", "2027")
- Quarterly tenors (1Q, 4Q, etc.) MUST use quarterly price keys ("2026Q1", "2026Q2")
Mixing formats will result in a validation error.
Q: What's the difference between Upstream and Downstream PPAs?
A:
- Upstream (tariffType: 5): You are selling energy. You provide energy to the market and receive payment.
- Downstream (tariffType: 6): You are buying energy. You purchase energy from producers and pay for it.
The JSON structure is identical; only the tariffType value differs.
Q: Can I combine Upstream and Downstream in one upload?
A: Yes, you can include multiple offers in the "offers" array, some with tariffType: 5 (Upstream) and others with tariffType: 6 (Downstream). See Example 4 for batch upload.
Q: How long can a PPA contract be?
A:
- Quarterly tenors can range from 1Q to 48Q (12 years) or longer
- Yearly tenors can be 1Y to 12Y or longer
The actual limit depends on your business requirements. The API supports extended durations for long-term PPA agreements.
Common Issues
❌ Wrong quarter format
"2026-Q2": { ... } // Wrong - has a dash
Fix: Remove the dash → "2026Q2": { ... }
❌ Invalid hedge percentage
"hedgeSharePercent": 120 // Wrong - must be 0-100
Fix: Use value between 0-100 → "hedgeSharePercent": 75
❌ Missing required price
"2026Q2": {
"guaranteeOfOriginFeeEurPerMWh": 0.5
// Missing priceEurPerMWh!
}
Fix: Add the price → "priceEurPerMWh": 64.0
❌ Wrong tariff type for PPA
"tariffType": 3 // Wrong - must be 5 or 6
Fix: Use 5 (Upstream) or 6 (Downstream)
Quick Reference: Allowed Values
tariffType
5- PPAUpstream (selling energy)6- PPADownstream (buying energy)
technology
"Solar"- Solar energy"Wind"- Wind energy"Biomass"- Biomass energy
ppaStructure
"PayAsForecasted"- Payment based on forecasted production"PayAsProduced"- Payment based on actual production
guaranteeOfOrigin
"None"- No GOO certificates"Provider"- Provider handles certificates"Customer"- Customer handles certificates
negativePrices
"Included"- Negative price hours included"Excluded"- Negative price hours excluded
Upstream vs. Downstream Guide
When to use Upstream (tariffType: 5)
- You are selling energy to the market
- Provider perspective - offering energy to customers
- Revenue generation - receiving payment for energy sold
When to use Downstream (tariffType: 6)
- You are buying energy from producers
- Customer perspective - purchasing energy for consumption
- Cost management - paying for energy purchased
Need Help?
- Check error messages for specific validation issues
- Validate JSON format before uploading
- Review examples for correct structure
- Ensure period formats match specifications
- Contact TRACE development team for support