PPA Upload API - Complete User Guide

Overview

This guide explains how to upload Power Purchase Agreement (PPA) offers to the system using JSON files. You can upload single or multiple offers with different configurations for capacity ranges, pricing models, and time periods.

Endpoint: POST /tariff-management/ppa/upload File Type: JSON uploaded as multipart/form-data

Note: For information on how versioning, price updates, and the contract-options API work, see the Price Upload Guide.


Table of Contents

  1. Quick Start
  2. What is a PPA Offer
  3. JSON Structure Overview
  4. Field Reference
  5. Complete JSON Examples - All Combinations
  6. How to Upload
  7. Response Format
  8. Validation Rules
  9. Common Mistakes
  10. Troubleshooting

Quick Start

  1. Create a JSON file with your PPA offer data
  2. Validate the JSON structure matches one of the examples below
  3. Upload using the API endpoint with Bearer authentication
  4. Check response for success or error messages

What is a PPA Offer

A PPA (Power Purchase Agreement) offer defines pricing and terms for buying or selling energy through long-term contracts:


JSON Structure Overview

Single Offer Upload

{
  "offer": {
    "name": "Offer Name",
    "tariffType": 5,
    "countryCode": "DE",
    "configuration": { ... },
    "fees": { ... },
    "priceMatrix": [ ... ]
  }
}

Batch Upload (Multiple Offers)

{
  "offers": [
    { "name": "First Offer", ... },
    { "name": "Second Offer", ... }
  ]
}

Note: Use either offer (singular) or offers (plural), not both.


Field Reference

Root Level

Field Type Required Description
offer object Yes* Single offer (for uploading one offer)
offers array Yes* Array of offers (for batch upload)

*Use one or the other, not both.


Offer Object

Field Type Required Description Example
name string Yes Descriptive name for the offer "Solar PPA Q2-Q4 2026"
tariffType integer Yes 5 for Upstream (selling), 6 for Downstream (buying) 5
countryCode string Yes ISO 3166-1 alpha-2 country code "DE", "AT"
description string No Optional detailed description "Solar PPA with GOO"
configuration object Yes Technical configuration See below
fees object Yes Default fee structure See below
priceMatrix array Yes Array of pricing contracts See below

Configuration Object

Field Type Required Allowed Values 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" Negative price hours handling
hedgeSharePercent decimal No 0-100 Percentage of production hedged
capacityTiers array No Array of capacity ranges Creates separate offers per tier

Capacity Tiers

capacityTiers is an array of capacity range objects. Each tier creates a separate offer in the database.

Capacity Range Object:

Field Type Required Description
min decimal No Minimum value (defaults to 0 if omitted). Unit depends on PPA type - see below.
max decimal No Maximum value (use null for unlimited). Unit depends on PPA type - see below.

Unit of Measurement:

Examples:

Important Behavior:

Offer Uniqueness Requirements

Each offer must have a unique configuration in the system. You cannot upload two offers with identical configuration settings (same combination of technology, ppaStructure, guaranteeOfOrigin, negativePrices, hedgeSharePercent, and capacityTiers).

If you need to provide different pricing schedules, group them as multiple contracts within the priceMatrix array of a single offer rather than creating separate offers with the same configuration. This approach:

Example: Instead of creating two separate "Solar PPA Upstream" offers with different start dates, create one offer with multiple contracts in the priceMatrix array, each with its own start period and pricing schedule.


Fees Object

Default fees that apply to all pricing periods unless overridden:

Field Type Required Description
guaranteeOfOriginFeeEurPerMWh decimal No Default Guarantee of Origin fee (EUR/MWh)
basicFeePerYear decimal No Default annual basic fee (EUR)

Price Matrix (Array of Contracts)

The priceMatrix is an array where each element represents a contract with a start period, duration (tenor), and pricing schedule.

Important: You can mix different time resolutions within the same priceMatrix. For example, you can have:

This allows you to provide multiple contract options with different pricing granularities.

Contract Object:

Field Type Required Description
start string Yes Start period: "YYYY" (yearly), "YYYYQN" (quarterly), or "YYYYMNN" (monthly)
tenor string No Contract duration: "3Y", "4Q", "12M", etc. (auto-calculated if omitted)
prices object Yes Pricing schedule with period keys

Start Period Formats:

Note: PPA supports yearly, quarterly, and monthly resolutions.

Tenor Formats:

Prices Object:

Keys are period identifiers (matching the start format). Each period contains pricing fields:

Field Type Required Description
priceEurPerMWh decimal Yes PPA price per MWh (EUR)
guaranteeOfOriginFeeEurPerMWh decimal No Override default GOO fee for this period

Note: priceEurPerMWh is required for each period.


Complete JSON Examples - All Combinations

1. Solar Upstream + PayAsForecasted + Quarterly + Multiple Capacity Tiers

Most common scenario: Creates 3 separate offers with capacity-based pricing.

{
  "offer": {
    "name": "Solar PPA Q2-Q4 2026",
    "tariffType": 5,
    "countryCode": "DE",
    "description": "Solar PPA with quarterly pricing and capacity tiers",
    "configuration": {
      "technology": "Solar",
      "ppaStructure": "PayAsForecasted",
      "guaranteeOfOrigin": "Provider",
      "negativePrices": "Included",
      "hedgeSharePercent": 75,
      "capacityTiers": [
        { "min": 0, "max": 499 },
        { "min": 500, "max": 999 },
        { "min": 1000, "max": null }
      ]
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0.5,
      "basicFeePerYear": 1200
    },
    "priceMatrix": [
      {
        "start": "2026Q2",
        "tenor": "3Q",
        "prices": {
          "2026Q2": {
            "priceEurPerMWh": 64.0,
            "guaranteeOfOriginFeeEurPerMWh": 0.5
          },
          "2026Q3": {
            "priceEurPerMWh": 65.0,
            "guaranteeOfOriginFeeEurPerMWh": 0.5
          },
          "2026Q4": {
            "priceEurPerMWh": 66.0,
            "guaranteeOfOriginFeeEurPerMWh": 0.5
          }
        }
      }
    ]
  }
}

Result: Creates 3 database offers:

Each offer has the same quarterly pricing schedule.


2. Wind Upstream + PayAsProduced + Yearly + No Capacity Tiers

Single offer without capacity restrictions: Creates 1 offer with yearly pricing.

{
  "offer": {
    "name": "Wind PPA 2027-2028",
    "tariffType": 5,
    "countryCode": "DE",
    "description": "Wind PPA with yearly pricing",
    "configuration": {
      "technology": "Wind",
      "ppaStructure": "PayAsProduced",
      "guaranteeOfOrigin": "Customer",
      "negativePrices": "Excluded",
      "hedgeSharePercent": 80
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0.3,
      "basicFeePerYear": 1500
    },
    "priceMatrix": [
      {
        "start": "2027",
        "tenor": "2Y",
        "prices": {
          "2027": {
            "priceEurPerMWh": 62.5
          },
          "2028": {
            "priceEurPerMWh": 63.2
          }
        }
      }
    ]
  }
}

Result: Creates 1 offer with no capacity restrictions.


3. Solar Downstream + PayAsForecasted + Monthly Pricing

Monthly granularity: 12-month contract with monthly price variations.

{
  "offer": {
    "name": "Solar PPA Monthly 2026",
    "tariffType": 6,
    "countryCode": "DE",
    "description": "Downstream solar PPA with monthly pricing",
    "configuration": {
      "technology": "Solar",
      "ppaStructure": "PayAsForecasted",
      "guaranteeOfOrigin": "Provider",
      "negativePrices": "Included",
      "hedgeSharePercent": 70,
      "capacityTiers": [
        { "min": 100, "max": 500 }
      ]
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0.5,
      "basicFeePerYear": 1200
    },
    "priceMatrix": [
      {
        "start": "2026M01",
        "tenor": "12M",
        "prices": {
          "2026M01": { "priceEurPerMWh": 62.5 },
          "2026M02": { "priceEurPerMWh": 63.0 },
          "2026M03": { "priceEurPerMWh": 63.5 },
          "2026M04": { "priceEurPerMWh": 64.0 },
          "2026M05": { "priceEurPerMWh": 64.5 },
          "2026M06": { "priceEurPerMWh": 65.0 },
          "2026M07": { "priceEurPerMWh": 65.5 },
          "2026M08": { "priceEurPerMWh": 66.0 },
          "2026M09": { "priceEurPerMWh": 65.5 },
          "2026M10": { "priceEurPerMWh": 65.0 },
          "2026M11": { "priceEurPerMWh": 64.5 },
          "2026M12": { "priceEurPerMWh": 64.0 }
        }
      }
    ]
  }
}

Result: Creates 1 offer with monthly pricing for the entire year 2026.


4. Biomass Upstream + Multiple Contracts (Mixed Time Resolutions)

Different time resolutions in the same priceMatrix: This example demonstrates quarterly and yearly contracts in one offer.

{
  "offer": {
    "name": "Biomass PPA 2026-2029",
    "tariffType": 5,
    "countryCode": "DE",
    "description": "Biomass PPA with mixed quarterly and yearly pricing",
    "configuration": {
      "technology": "Biomass",
      "ppaStructure": "PayAsProduced",
      "guaranteeOfOrigin": "None",
      "negativePrices": "Included",
      "hedgeSharePercent": 100,
      "capacityTiers": [
        { "min": 500, "max": null }
      ]
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0,
      "basicFeePerYear": 2000
    },
    "priceMatrix": [
      {
        "start": "2026Q3",
        "tenor": "2Q",
        "prices": {
          "2026Q3": { "priceEurPerMWh": 70.0 },
          "2026Q4": { "priceEurPerMWh": 71.0 }
        }
      },
      {
        "start": "2027",
        "tenor": "3Y",
        "prices": {
          "2027": { "priceEurPerMWh": 68.0 },
          "2028": { "priceEurPerMWh": 69.0 },
          "2029": { "priceEurPerMWh": 70.0 }
        }
      }
    ]
  }
}

Result: Creates 1 offer with two contract options at different time resolutions:


5. Wind Downstream + PayAsProduced + Full Year Quarterly

All 4 quarters for a full year:

{
  "offer": {
    "name": "Wind PPA Full Year 2027",
    "tariffType": 6,
    "countryCode": "DE",
    "description": "Downstream wind PPA covering all quarters of 2027",
    "configuration": {
      "technology": "Wind",
      "ppaStructure": "PayAsProduced",
      "guaranteeOfOrigin": "Customer",
      "negativePrices": "Excluded",
      "hedgeSharePercent": 85
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0.4,
      "basicFeePerYear": 1800
    },
    "priceMatrix": [
      {
        "start": "2027Q1",
        "tenor": "4Q",
        "prices": {
          "2027Q1": { "priceEurPerMWh": 60.0 },
          "2027Q2": { "priceEurPerMWh": 61.0 },
          "2027Q3": { "priceEurPerMWh": 62.0 },
          "2027Q4": { "priceEurPerMWh": 63.0 }
        }
      }
    ]
  }
}

Result: Creates 1 offer with all 4 quarters of 2027.


6. Solar Upstream + No Hedge + Single Capacity Tier

100% hedged or 0% hedged scenarios:

{
  "offer": {
    "name": "Solar PPA Unhedged Large Scale",
    "tariffType": 5,
    "countryCode": "DE",
    "description": "Large-scale solar with no hedging",
    "configuration": {
      "technology": "Solar",
      "ppaStructure": "PayAsForecasted",
      "guaranteeOfOrigin": "Provider",
      "negativePrices": "Included",
      "hedgeSharePercent": 0,
      "capacityTiers": [
        { "min": 2000, "max": null }
      ]
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0.2,
      "basicFeePerYear": 3000
    },
    "priceMatrix": [
      {
        "start": "2026",
        "tenor": "3Y",
        "prices": {
          "2026": { "priceEurPerMWh": 58.0 },
          "2027": { "priceEurPerMWh": 58.5 },
          "2028": { "priceEurPerMWh": 59.0 }
        }
      }
    ]
  }
}

Result: Creates 1 offer named "Solar PPA Unhedged Large Scale (>2000kW)" with 0% hedging.


7. Wind Upstream + Auto-Calculated Tenor

No tenor specified: System calculates tenor from the number of price periods.

{
  "offer": {
    "name": "Wind PPA Auto-Tenor",
    "tariffType": 5,
    "countryCode": "DE",
    "description": "Contract with automatically calculated tenor",
    "configuration": {
      "technology": "Wind",
      "ppaStructure": "PayAsProduced",
      "guaranteeOfOrigin": "None",
      "negativePrices": "Included"
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0,
      "basicFeePerYear": 1500
    },
    "priceMatrix": [
      {
        "start": "2026Q2",
        "prices": {
          "2026Q2": { "priceEurPerMWh": 62.0 },
          "2026Q3": { "priceEurPerMWh": 63.0 },
          "2026Q4": { "priceEurPerMWh": 64.0 },
          "2027Q1": { "priceEurPerMWh": 65.0 }
        }
      }
    ]
  }
}

Result: Creates 1 offer. Tenor is calculated as "4Q" (4 quarterly price periods).


8. Batch Upload - Multiple Offers at Once

Upload multiple offers in one request:

{
  "offers": [
    {
      "name": "Solar PPA Small Scale",
      "tariffType": 5,
      "countryCode": "DE",
      "description": "Small solar installations",
      "configuration": {
        "technology": "Solar",
        "ppaStructure": "PayAsForecasted",
        "guaranteeOfOrigin": "Provider",
        "negativePrices": "Included",
        "hedgeSharePercent": 75,
        "capacityTiers": [
          { "min": 0, "max": 200 }
        ]
      },
      "fees": {
        "guaranteeOfOriginFeeEurPerMWh": 0.5,
        "basicFeePerYear": 800
      },
      "priceMatrix": [
        {
          "start": "2026",
          "tenor": "1Y",
          "prices": {
            "2026": { "priceEurPerMWh": 66.0 }
          }
        }
      ]
    },
    {
      "name": "Wind PPA Large Scale",
      "tariffType": 5,
      "countryCode": "DE",
      "description": "Large wind installations",
      "configuration": {
        "technology": "Wind",
        "ppaStructure": "PayAsProduced",
        "guaranteeOfOrigin": "Customer",
        "negativePrices": "Excluded",
        "hedgeSharePercent": 90,
        "capacityTiers": [
          { "min": 5000, "max": null }
        ]
      },
      "fees": {
        "guaranteeOfOriginFeeEurPerMWh": 0.3,
        "basicFeePerYear": 5000
      },
      "priceMatrix": [
        {
          "start": "2027Q1",
          "tenor": "8Q",
          "prices": {
            "2027Q1": { "priceEurPerMWh": 60.0 },
            "2027Q2": { "priceEurPerMWh": 61.0 },
            "2027Q3": { "priceEurPerMWh": 62.0 },
            "2027Q4": { "priceEurPerMWh": 63.0 },
            "2028Q1": { "priceEurPerMWh": 62.5 },
            "2028Q2": { "priceEurPerMWh": 63.5 },
            "2028Q3": { "priceEurPerMWh": 64.5 },
            "2028Q4": { "priceEurPerMWh": 65.5 }
          }
        }
      ]
    }
  ]
}

Result: Creates 2 separate offers. Both are validated and imported in one request.


9. Solar Downstream + Mixed Yearly/Quarterly/Monthly

All three time resolutions in one offer:

{
  "offer": {
    "name": "Solar PPA Multi-Resolution",
    "tariffType": 6,
    "countryCode": "DE",
    "description": "Demonstrates yearly, quarterly, and monthly contracts",
    "configuration": {
      "technology": "Solar",
      "ppaStructure": "PayAsForecasted",
      "guaranteeOfOrigin": "Provider",
      "negativePrices": "Included",
      "hedgeSharePercent": 75,
      "capacityTiers": [
        { "min": 500, "max": 1500 }
      ]
    },
    "fees": {
      "guaranteeOfOriginFeeEurPerMWh": 0.5,
      "basicFeePerYear": 1500
    },
    "priceMatrix": [
      {
        "start": "2026",
        "tenor": "2Y",
        "prices": {
          "2026": { "priceEurPerMWh": 64.0 },
          "2027": { "priceEurPerMWh": 65.0 }
        }
      },
      {
        "start": "2028Q1",
        "tenor": "4Q",
        "prices": {
          "2028Q1": { "priceEurPerMWh": 66.0 },
          "2028Q2": { "priceEurPerMWh": 67.0 },
          "2028Q3": { "priceEurPerMWh": 68.0 },
          "2028Q4": { "priceEurPerMWh": 69.0 }
        }
      },
      {
        "start": "2029M01",
        "tenor": "6M",
        "prices": {
          "2029M01": { "priceEurPerMWh": 70.0 },
          "2029M02": { "priceEurPerMWh": 70.5 },
          "2029M03": { "priceEurPerMWh": 71.0 },
          "2029M04": { "priceEurPerMWh": 71.5 },
          "2029M05": { "priceEurPerMWh": 72.0 },
          "2029M06": { "priceEurPerMWh": 72.5 }
        }
      }
    ]
  }
}

Result: Creates 1 offer with three contract options at different time resolutions:


10. Biomass Upstream + Minimal Fields

Minimum required fields only:

{
  "offer": {
    "name": "Biomass PPA Minimal",
    "tariffType": 5,
    "countryCode": "DE",
    "configuration": {
      "technology": "Biomass",
      "ppaStructure": "PayAsProduced",
      "guaranteeOfOrigin": "None",
      "negativePrices": "Included"
    },
    "fees": {},
    "priceMatrix": [
      {
        "start": "2027",
        "prices": {
          "2027": { "priceEurPerMWh": 72.0 }
        }
      }
    ]
  }
}

Result: Creates 1 offer with minimal configuration. No hedge percentage, no fees, single year pricing.


How to Upload

Using cURL (Command Line)

curl -X POST "https://your-api-domain.com/tariff-management/ppa/upload" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: multipart/form-data" \
  -F "file=@your-ppa-offer.json"

Using PowerShell

$filePath = 'C:\path\to\your-ppa-offer.json'
$uri = 'https://your-api-domain.com/tariff-management/ppa/upload'
$headers = @{ Authorization = 'Bearer YOUR_TOKEN' }
$form = @{ file = Get-Item $filePath }

Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Form $form

Using Postman

  1. Method: POST
  2. URL: https://your-api-domain.com/tariff-management/ppa/upload
  3. Headers:
    • Authorization: Bearer YOUR_TOKEN
  4. Body:
    • Select form-data
    • Key: file (change type to File)
    • Value: Select your JSON file
  5. Send

Using JavaScript (Fetch API)

const formData = new FormData();
formData.append('file', fileInput.files[0]);

fetch('https://your-api-domain.com/tariff-management/ppa/upload', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  },
  body: formData
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('Error:', error));

Response Format

Success Response

When using capacityTiers, multiple results are returned - one per tier:

{
  "success": true,
  "results": [
    {
      "offerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "success": true,
      "message": "Imported successfully"
    },
    {
      "offerId": "7bc12d89-8a23-4c71-9def-5e432a1b8c92",
      "success": true,
      "message": "Imported successfully"
    },
    {
      "offerId": "9de45c01-2f34-4a87-8bcd-4d567e8f9a01",
      "success": true,
      "message": "Imported successfully"
    }
  ]
}

Without Capacity Tiers

Single result when no tiers are specified:

{
  "success": true,
  "results": [
    {
      "offerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "success": true,
      "message": "Imported successfully"
    }
  ]
}

Batch Upload Response

Multiple results for batch uploads:

{
  "success": true,
  "results": [
    {
      "offerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "success": true,
      "message": "Imported successfully"
    },
    {
      "offerId": "7bc12d89-8a23-4c71-9def-5e432a1b8c92",
      "success": true,
      "message": "Imported successfully"
    }
  ]
}

Error Response

If validation fails:

{
  "success": false,
  "results": [
    {
      "offerId": null,
      "success": false,
      "message": "Validation failed: TariffType must be 5 (Upstream) or 6 (Downstream) for PPA"
    }
  ]
}

Validation Rules

Required Fields

offer or offers must be present ✓ name, tariffType, countryCode required for each offer ✓ configuration with all required fields: technology, ppaStructure, guaranteeOfOrigin, negativePricesfees object (can be empty {}) ✓ priceMatrix with at least one contract ✓ Each contract must have start and prices ✓ Each price entry must have priceEurPerMWh

Field Validation

tariffType must be 5 (Upstream) or 6 (Downstream) ✓ countryCode must be valid ISO 3166-1 alpha-2 (2 letters) ✓ Enum values must match exactly (case-sensitive): "Solar", "Wind", "Biomass", "PayAsForecasted", "PayAsProduced", "None", "Provider", "Customer", "Included", "Excluded" ✓ Period keys in prices must match start format (yearly: "YYYY", quarterly: "YYYYQN", monthly: "YYYYMNN") ✓ hedgeSharePercent must be between 0 and 100 (if provided)

Capacity Tiers

✓ If provided, min must be less than max (when both are non-null) ✓ min and max must be non-negative ✓ Tiers can be omitted entirely (creates single offer without capacity restrictions)

Pricing

priceEurPerMWh is required for each period entry ✓ Decimal values must be valid numbers

Contract Logic

tenor is optional (auto-calculated from price periods if omitted) ✓ Multiple contracts can have overlapping or different time ranges


Common Mistakes

Wrong tariffType

"tariffType": 3  // WRONG - 3 is Direct Marketing, not PPA

Fix:

"tariffType": 5  // Correct for PPA Upstream (selling)
// OR
"tariffType": 6  // Correct for PPA Downstream (buying)

Invalid quarter format

"prices": {
  "2026-Q2": { "priceEurPerMWh": 64.0 }  // WRONG - has a dash
}

Fix:

"prices": {
  "2026Q2": { "priceEurPerMWh": 64.0 }  // Correct - no dash
}

Invalid hedge percentage

"hedgeSharePercent": 120  // WRONG - must be 0-100

Fix:

"hedgeSharePercent": 75  // Correct - between 0 and 100

Missing required price field

"prices": {
  "2026Q2": { "guaranteeOfOriginFeeEurPerMWh": 0.5 }  // WRONG - missing priceEurPerMWh
}

Fix:

"prices": {
  "2026Q2": {
    "priceEurPerMWh": 64.0,  // Required
    "guaranteeOfOriginFeeEurPerMWh": 0.5
  }
}

Mismatched period keys

{
  "start": "2026",      // Yearly format
  "prices": {
    "2026Q1": { ... }  // WRONG - Quarterly format doesn't match
  }
}

Fix:

{
  "start": "2026",
  "prices": {
    "2026": { ... },    // Match yearly format
    "2027": { ... }
  }
}

Using both offer and offers

{
  "offer": { ... },   // WRONG - Can't use both
  "offers": [ ... ]
}

Fix (choose one):

// For single offer:
{
  "offer": { ... }
}

// For batch:
{
  "offers": [ ... ]
}

Invalid month or quarter number

"start": "2026Q5"  // WRONG - Quarter must be 1-4
"start": "2026M13" // WRONG - Month must be 01-12

Fix:

"start": "2026Q4"  // Correct
"start": "2026M12" // Correct

Can I mix yearly, quarterly, and monthly contracts in the same offer?

Yes! You can have multiple contracts with different time resolutions in the same priceMatrix:

"priceMatrix": [
  {
    "start": "2026",
    "tenor": "2Y",
    "prices": {
      "2026": { ... },  // Yearly
      "2027": { ... }   // Yearly
    }
  },
  {
    "start": "2028Q1",
    "tenor": "4Q",
    "prices": {
      "2028Q1": { ... },  // Quarterly
      "2028Q2": { ... },  // Quarterly
      "2028Q3": { ... },  // Quarterly
      "2028Q4": { ... }   // Quarterly
    }
  },
  {
    "start": "2029M01",
    "tenor": "6M",
    "prices": {
      "2029M01": { ... },  // Monthly
      "2029M02": { ... },  // Monthly
      "2029M03": { ... }   // Monthly
      // ...
    }
  }
]

This creates one offer with three contract options at different time granularities.


Troubleshooting

Issue: "TariffType must be 5 or 6"

Cause: You used a different tariff type number. Fix: Set "tariffType": 5 (Upstream/selling) or "tariffType": 6 (Downstream/buying)


Issue: "Invalid enumeration value"

Cause: Misspelled enum value or used wrong case. Fix: Use exact strings: "Solar", "Wind", "Biomass", "PayAsForecasted", "PayAsProduced", "None", "Provider", "Customer", "Included", "Excluded"


Issue: "HedgeSharePercent must be between 0 and 100"

Cause: Value outside valid range. Fix: Use a value between 0 and 100, or omit the field entirely.


Issue: "priceEurPerMWh is required"

Cause: Missing price for a period. Fix: Add priceEurPerMWh for each price entry.


Issue: "Invalid JSON format"

Cause: Syntax error in JSON (missing comma, bracket, etc.). Fix: Validate JSON at jsonlint.com or use a JSON validator.


Issue: Multiple offers created unexpectedly

Cause: You provided capacityTiers in correct format. Explanation: This is expected behavior. Each tier creates a separate offer. Fix (if unwanted): Remove capacityTiers to create single offer without capacity restrictions.


Issue: "Tenor calculation failed"

Cause: Provided invalid or inconsistent price periods. Fix: Ensure price period keys are sequential and match the start format. Or explicitly provide tenor.


Summary Checklist

Before uploading:


Last Updated: December 2025 Endpoint: /tariff-management/ppa/upload