initial draft commit

This commit is contained in:
2026-05-20 16:47:05 +02:00
parent c43f3fc948
commit e0b1da2149
7 changed files with 525 additions and 0 deletions

3
.gitignore vendored
View File

@@ -12,3 +12,6 @@
# Built Visual Studio Code Extensions # Built Visual Studio Code Extensions
*.vsix *.vsix
iso*.csv
domains_operations.txt
merge_providers.py

View File

View File

@@ -1,2 +1,68 @@
# emission-api-lib # emission-api-lib
`emission-api-lib` is the authoritative draft specification for provider-neutral emission calculation connectors.
The project defines a shared domain model, provider configuration format, provider capability vocabulary and validation artifacts for integrating multiple emission calculation providers through a stable connector layer.
## Status
Draft specification.
The specification is not stable yet. Breaking changes are expected until the first stable version is released.
## Purpose
Direct integrations between specialist applications and individual emission calculation APIs create long-term dependencies on provider-specific request formats, response formats and feature assumptions.
`emission-api-lib` defines a provider-neutral integration boundary. Applications should be able to express an emission calculation request in a stable domain format, while provider-specific mappings, capabilities and limitations are handled by connector implementations.
## Repository role
This repository is authoritative.
Language-specific repositories implement this specification, but do not define it.
- `emission-api-lib-java` is the Java reference implementation.
- `emission-api-lib-python` is the Python implementation.
- `emission-api-lib-php` is the PHP implementation.
All implementation repositories should link back to this repository.
## Provider scope
The specification is designed for multiple providers from the beginning.
A provider is described through XML configuration and machine-readable capability metadata. Implementations may use this information to validate requests, select providers, explain unsupported features and normalize responses.
## Specification artifacts
This repository may contain:
- `schema.json` for the shared domain model
- XSD files for provider XML validation
- provider XML examples
- normalized request and response examples
- provider capability definitions
- compatibility notes for language-specific implementations
## Initial domain
The first target domain is flight emission calculation.
The specification is expected to support provider capability discovery instead of assuming that every provider supports the same request structure, response structure or calculation options.
## Design principles
- Provider neutrality
- Multiple providers from the beginning
- Stable domain-level request and response concepts
- Explicit provider capabilities
- Configuration over hardcoded provider logic
- Replayability and auditability where supported
- Language implementations that follow the shared specification
## License
The general specification repository is licensed under the GNU Affero General Public License, Version 3 or later.
Individual implementation repositories may use a different license if this improves integration and adoption. Each implementation repository must state its license clearly.

97
providers.xml Normal file
View File

@@ -0,0 +1,97 @@
<?xml version='1.0' encoding='utf-8'?>
<providers xmlns="https://calco2la.to/schema/providers/v1">
<provider id="calco2lato">
<name>calco2la.to</name>
<baseUrl>https://api.calco2la.to</baseUrl>
<auth type="apiKey">
<header>Authorization</header>
<format>Bearer ${API_KEY}</format>
</auth>
<operations>
<operation id="travel.flight.estimate_emissions">
<http method="POST" path="/v1/flight/estimate" />
<requestBody format="json">
{
"legs": [
#for leg in request.legs
{
"origin": "${leg.origin_iata}",
"destination": "${leg.destination_iata}",
"departure_time": "${leg.departure_time}"
}#sep,
#end
],
"cabin_class": "${request.cabin_class}",
"passengers": ${request.passengers},
"include_non_co2": ${request.include_non_co2}
}
</requestBody>
<responseMapping>
<map source="$.co2_kg" target="EmissionEstimate.co2_kg" />
<map source="$.co2e_kg" target="EmissionEstimate.co2e_kg" />
<map source="$.non_co2_mult" target="EmissionEstimate.non_co2_multiplier" />
<map source="$.method.name" target="EmissionEstimate.method_name" />
<map source="$.method.version" target="EmissionEstimate.method_version" />
<map source="$.standard" target="EmissionEstimate.standard" />
<map source="$.documentation_url" target="EmissionEstimate.documentation_url" />
<map source="$" target="EmissionEstimate.vendor_raw" />
</responseMapping>
</operation>
<operation id="travel.airport.search">
<http method="GET" path="/v1/airports/search" />
<requestQuery>
<param name="q" from="AirportSearchRequest.query" />
<param name="limit" from="AirportSearchRequest.limit" />
</requestQuery>
<responseMapping>
<list target="AirportInfo">
<item source="$.results[*]">
<map source="$.iata" target="AirportInfo.iata_code" />
<map source="$.icao" target="AirportInfo.icao_code" />
<map source="$.name" target="AirportInfo.name" />
<map source="$.city" target="AirportInfo.city" />
<map source="$.country" target="AirportInfo.country" />
<map source="$.lat" target="AirportInfo.latitude" />
<map source="$.lon" target="AirportInfo.longitude" />
</item>
</list>
</responseMapping>
</operation>
</operations>
</provider>
<provider id="google_tim">
<name>Google Travel Impact Model</name>
<baseUrl>https://travelimpactmodel.googleapis.com/v1</baseUrl>
<auth type="apiKey">
<header>X-Goog-Api-Key</header>
<format>${API_KEY}</format>
</auth>
<operations>
<operation id="travel.flight.estimate_emissions">
<http method="POST" path="/flights:computeFlightEmissions" />
<requestBody format="json">
{
"flightSegments": [
#for leg in request.legs
{
"departureAirport": { "code": "${leg.origin_iata}" },
"arrivalAirport": { "code": "${leg.destination_iata}" }
}#sep,
#end
],
"cabinClass": "${request.cabin_class}",
"passengerCount": ${request.passengers}
}
</requestBody>
<responseMapping>
<map source="$.flightEmissions[0].co2Grams" target="EmissionEstimate.co2_kg" transform="divideBy1000" />
<map source="$.flightEmissions[0].co2eGrams" target="EmissionEstimate.co2e_kg" transform="divideBy1000" />
<map source="$.modelVersion" target="EmissionEstimate.method_version" />
<constant target="EmissionEstimate.method_name" value="Travel Impact Model" />
<constant target="EmissionEstimate.vendor" value="google_tim" />
<map source="$" target="EmissionEstimate.vendor_raw" />
</responseMapping>
</operation>
</operations>
</provider>
</providers>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<providers xmlns="https://calco2la.to/schema/providers/v1">
<provider id="calco2lato">
<name>calco2la.to</name>
<baseUrl>https://api.calco2la.to</baseUrl>
<auth type="apiKey">
<header>Authorization</header>
<format>Bearer ${API_KEY}</format>
</auth>
<operations>
<!-- travel.flight.estimate_emissions -->
<operation id="travel.flight.estimate_emissions">
<http method="POST" path="/v1/flight/estimate" />
<!-- Template-based request body in provider's JSON shape -->
<requestBody format="json">
{
"legs": [
#for leg in request.legs
{
"origin": "${leg.origin_iata}",
"destination": "${leg.destination_iata}",
"departure_time": "${leg.departure_time}"
}#sep,
#end
],
"cabin_class": "${request.cabin_class}",
"passengers": ${request.passengers},
"include_non_co2": ${request.include_non_co2}
}
</requestBody>
<!-- How to interpret provider's JSON response -->
<responseMapping>
<map source="$.co2_kg" target="EmissionEstimate.co2_kg" />
<map source="$.co2e_kg" target="EmissionEstimate.co2e_kg" />
<map source="$.non_co2_mult" target="EmissionEstimate.non_co2_multiplier" />
<map source="$.method.name" target="EmissionEstimate.method_name" />
<map source="$.method.version" target="EmissionEstimate.method_version" />
<map source="$.standard" target="EmissionEstimate.standard" />
<map source="$.documentation_url" target="EmissionEstimate.documentation_url" />
<!-- store full JSON -->
<map source="$" target="EmissionEstimate.vendor_raw" />
</responseMapping>
</operation>
<!-- travel.airport.search -->
<operation id="travel.airport.search">
<http method="GET" path="/v1/airports/search" />
<requestQuery>
<param name="q" from="AirportSearchRequest.query" />
<param name="limit" from="AirportSearchRequest.limit" />
</requestQuery>
<responseMapping>
<!-- array mapping: one AirportInfo per item in $.results[] -->
<list target="AirportInfo">
<item source="$.results[*]">
<map source="$.iata" target="AirportInfo.iata_code" />
<map source="$.icao" target="AirportInfo.icao_code" />
<map source="$.name" target="AirportInfo.name" />
<map source="$.city" target="AirportInfo.city" />
<map source="$.country" target="AirportInfo.country" />
<map source="$.lat" target="AirportInfo.latitude" />
<map source="$.lon" target="AirportInfo.longitude" />
</item>
</list>
</responseMapping>
</operation>
</operations>
</provider>
</providers>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<providers xmlns="https://calco2la.to/schema/providers/v1">
<!-- Google TIM -->
<provider id="google_tim">
<name>Google Travel Impact Model</name>
<baseUrl>https://travelimpactmodel.googleapis.com/v1</baseUrl>
<auth type="apiKey">
<header>X-Goog-Api-Key</header>
<format>${API_KEY}</format>
</auth>
<operations>
<operation id="travel.flight.estimate_emissions">
<http method="POST" path="/flights:computeFlightEmissions" />
<requestBody format="json">
{
"flightSegments": [
#for leg in request.legs
{
"departureAirport": { "code": "${leg.origin_iata}" },
"arrivalAirport": { "code": "${leg.destination_iata}" }
}#sep,
#end
],
"cabinClass": "${request.cabin_class}",
"passengerCount": ${request.passengers}
}
</requestBody>
<responseMapping>
<map source="$.flightEmissions[0].co2Grams"
target="EmissionEstimate.co2_kg"
transform="divideBy1000" />
<map source="$.flightEmissions[0].co2eGrams"
target="EmissionEstimate.co2e_kg"
transform="divideBy1000" />
<map source="$.modelVersion"
target="EmissionEstimate.method_version" />
<constant target="EmissionEstimate.method_name" value="Travel Impact Model" />
<constant target="EmissionEstimate.vendor" value="google_tim" />
<map source="$" target="EmissionEstimate.vendor_raw" />
</responseMapping>
</operation>
</operations>
</provider>
</providers>

232
scheme.json Normal file
View File

@@ -0,0 +1,232 @@
{
"FlightLeg": {
"origin_iata": "string",
"destination_iata": "string",
"departure_time": "optional ISO-8601 datetime",
"marketing_carrier": "optional string",
"operating_carrier": "optional string",
"flight_number": "optional string"
},
"FlightRequest": {
"legs": ["FlightLeg"],
"cabin_class": "economy | premium_economy | business | first | unknown",
"passengers": "integer >= 1",
"roundtrip": "boolean",
"include_non_co2": "boolean",
"currency": "optional ISO 4217 code"
},
"EmissionEstimate": {
"co2_kg": "number or null",
"co2e_kg": "number or null",
"non_co2_multiplier": "number or null",
"breakdown": {
"ch4_kg": "number or null",
"n2o_kg": "number or null"
},
"methodology": {
"method_name": "string or null",
"method_version": "string or null",
"dataset_version": "string or null",
"vendor": "string or null",
"standard": "string or null",
"documentation_url": "string or null"
},
"vendor_raw": "opaque vendor-specific JSON/XML"
},
"AirportSearchRequest": {
"query": "string",
"limit": "optional integer",
"country_filter": "optional ISO 3166 code",
"languages": ["optional ISO 639 code"]
},
"AirportInfo": {
"iata_code": "string",
"icao_code": "string",
"name": "string",
"localized_name": ["LocalizedString"],
"country": "ISO 3166 code",
"continent": "string",
"latitude": "double",
"longitude": "double"
},
"LocalizedString": {
"lang": "ISO 639 code",
"name": "string"
}
}
{
"FlightCalculationRequest": {
"api_key": "optional string",
"provider": "optional string",
"operation": "optional string",
"departure_date": "optional ISO-8601 date",
"rfi": "optional number",
"price_per_ton": "optional number",
"currency": "optional ISO 4217 code",
"roundtrip": "optional boolean",
"passengers": "optional integer >= 1",
"cabin_class": "economy | premium_economy | business | first | unknown",
"calculation_method": "optional string",
"reference": "optional string",
"legs": ["FlightLeg"],
"iata_path": ["string"],
"currencies": ["optional ISO 4217 code"],
"vendor_options": "object"
},
"FlightLeg": {
"id": "optional string",
"index": "optional integer",
"origin_iata": "optional string",
"destination_iata": "optional string",
"origin": "optional LocationRef",
"destination": "optional LocationRef",
"departure_date": "optional ISO-8601 date",
"departure_time": "optional ISO-8601 datetime",
"flight_number": "optional string",
"marketing_carrier": "optional string",
"operating_carrier": "optional string",
"airline": "optional string",
"aircraft_type": "optional string",
"passenger_count": "optional integer",
"flight_count": "optional integer",
"travel_class": "optional string",
"charter": "optional boolean",
"via": ["optional string"],
"distance_km": "optional number",
"vendor_options": "object"
},
"EmissionCalculationResult": {
"provider": "string",
"domain": "string",
"operation": "string",
"status": "string",
"total": "EmissionAmount",
"per_passenger": "EmissionAmount",
"distance": "DistanceAmount",
"per_passenger_distance": "DistanceAmount",
"fuel": "FuelAmount",
"offset": "MoneyAmount",
"per_passenger_price": "MoneyAmount",
"quote": "QuoteInfo",
"methodology": "MethodologyMeta",
"passenger_count": "integer or null",
"travel_class": "string or null",
"description": "string or null",
"segments": ["SegmentEmissionResult"],
"prices": ["MoneyAmount"],
"errors": ["ProviderError"],
"metadata": "opaque object or array",
"input_echo": "opaque object",
"vendor_raw": "opaque object"
},
"SegmentEmissionResult": {
"id": "string or null",
"index": "integer or null",
"type": "string or null",
"status": "string or null",
"origin": "LocationRef",
"destination": "LocationRef",
"flight_number": "string or null",
"flight_date": "ISO-8601 date or null",
"airline": "string or null",
"aircraft_type": "string or null",
"passenger_count": "integer or null",
"flight_count": "integer or null",
"travel_class": "string or null",
"charter": "boolean or null",
"emissions": "EmissionAmount",
"class_emissions": "CabinClassEmissionSet",
"distance": "DistanceAmount",
"fuel": "FuelAmount",
"offset": "MoneyAmount",
"cruise_altitude": "number or null",
"distance_in_critical_altitudes": "number or null",
"source": "string or null",
"errors": ["ProviderError"],
"vendor_raw": "opaque object"
},
"EmissionAmount": {
"co2_kg": "number or null",
"co2e_kg": "number or null",
"co2e_tonnes": "number or null",
"wtw_kg": "number or null",
"ttw_kg": "number or null",
"wtt_kg": "number or null",
"non_co2_multiplier": "number or null",
"unit": "kg | tonnes | grams | null",
"per": "request | passenger | segment | null"
},
"CabinClassEmissionSet": {
"economy_kg": "number or null",
"premium_economy_kg": "number or null",
"business_kg": "number or null",
"first_kg": "number or null"
},
"DistanceAmount": {
"value": "number or null",
"unit": "km | miles | null",
"miles": "number or null"
},
"FuelAmount": {
"liters": "number or null",
"kg": "number or null"
},
"MoneyAmount": {
"amount": "number or null",
"amount_minor": "integer or null",
"currency": "ISO 4217 code or null",
"url": "string or null",
"locale": "string or null"
},
"QuoteInfo": {
"id": "string or null",
"expires_at": "ISO-8601 datetime or null"
},
"MethodologyMeta": {
"method_name": "string or null",
"method_version": "string or null",
"dataset_version": "string or null",
"vendor": "string or null",
"standard": "string or null",
"documentation_url": "string or null",
"non_co2_treatment": "none | included | partial | unknown"
},
"ProviderError": {
"code": "string or null",
"message": "string",
"field": "string or null",
"vendor_raw": "opaque object"
},
"LocationRef": {
"iata": "string or null",
"icao": "string or null",
"name": "string or null",
"city": "string or null",
"country": "ISO 3166 code or null",
"continent": "string or null",
"lat": "number or null",
"lon": "number or null",
"coord": "string or null"
},
"AirportSearchRequest": {
"query": "string",
"limit": "optional integer",
"country_filter": "optional ISO 3166 code",
"languages": ["optional ISO 639 code"]
},
"AirportInfo": {
"iata_code": "string",
"icao_code": "string",
"name": "string",
"localized_name": ["LocalizedString"],
"country": "ISO 3166 code",
"continent": "string",
"latitude": "double",
"longitude": "double"
},
"LocalizedString": {
"lang": "ISO 639 code",
"name": "string"
}
}