# Aerovolt EBC API The external API lets another system create mission-backed carbon certificate records for flights. Base URL on production: ```text https://app.aerovolt.co.uk ``` ## Authentication External endpoints require API client credentials. Use either HTTP Basic Auth: ```text Authorization: Basic base64(CLIENT_KEY:CLIENT_SECRET) ``` or headers: ```text X-Carbon-Key: CLIENT_KEY X-Carbon-Secret: CLIENT_SECRET ``` API credentials are issued by the logged-in user from the app settings API Credentials popup. ## Create Certificate ```text POST /api/external/certificates/create Content-Type: application/json ``` Creates: - a mission - mission legs - a certificate record - certificate URL - PDF URL Private records are supported with `"public_status": "private"`. They do not appear in the Public Registry or partial public search. They can only be found by entering the full certificate number, and the aircraft registration is displayed as `PRIVATE` on the certificate page and PDF. First creation returns `201 Created`. A retry with the same `external_reference`, `idempotency_key`, supplied `certificate_number`, `record_reference`, `record_id`, `reference`, `booking_reference`, or `customer_reference` returns `200 OK` and `already_exists: true`. ## Recommended Payload ```json { "external_reference": "BOOKING-10428", "record_reference": "SUPPLIER-INV-7781", "customer_reference": "BOOKING-10428", "mission_number": "API-10428", "reference_number": "OPS-10428", "public_status": "public", "co2_used_t": 12.42, "ebc_allocated_t": 10.0, "value_purchased": 2400.0, "currency": "GBP", "supplier_commission_percent": 12.5, "payment_status": "unpaid", "upsert": true, "aircraft": { "manufacturer": "Gulfstream Aerospace", "model": "G650", "variant": "G650" }, "flights": [ { "flight_number": "AV001", "departure": { "iata": "LHR" }, "arrival": { "iata": "NCE" }, "departure_time": "2026-06-03 08:00:00", "arrival_time": "2026-06-03 10:05:00" }, { "flight_number": "AV002", "departure": { "iata": "NCE" }, "arrival": { "iata": "LCY" }, "departure_time": "2026-06-04 09:00:00", "arrival_time": "2026-06-04 10:45:00", "aircraft": { "manufacturer": "Cessna", "model": "Citation CJ4" } } ] } ``` The commercial/carbon fields above are the important record values: - `co2_used_t`: total CO2 used on the flight, in tonnes. Optional when enough aircraft and route data is supplied for the API to calculate it. - `ebc_allocated_t`: amount allocated/purchased as EBC, in tonnes. Optional for carbon-report-only records. - `value_purchased`: monetary value of the EBC purchase in the submitted currency. Optional for carbon-report-only records. - `currency`: 3-letter ISO currency for `value_purchased`, such as `GBP`, `EUR`, or `USD`. If omitted, `USD` is assumed. - `supplier_commission_percent`: supplier commission percentage to record against the EBC value. - `supplier_commission_amount`: supplier commission amount when it is supplied as a fixed value instead of a percent. - `supplier_commission_currency`: 3-letter ISO currency for `supplier_commission_amount`. If omitted, the submitted EBC value currency is used. - `record_reference`: supplier, booking, invoice, or external record reference to store against the certificate. - `payment_status`: payment state for the certificate record. Defaults to `unpaid`. - `upsert`: when `true`, a repeat submission with the same external, certificate, record, booking, or customer reference updates the existing record instead of creating a duplicate. If CO2 is omitted, the API calculates CO2 from aircraft/flight data. It resolves the aircraft from the aircraft database, calculates route distance from airport coordinates, and estimates CO2 from aircraft fuel burn or electric CO2-per-hour data. Response totals include `co2_source` as `payload` or `aircraft_database`. The API returns point-to-point distance as `totals.point_to_point_distance_nm` and `totals.distance_nm`, and each leg includes `point_to_point_distance_nm` / `distance_nm`. Aircraft matching is strict. Use exact `manufacturer` + `model` + optional `variant` values from the API Documentation aircraft glossary. These values come from the `app.aircraft_models` table. Misspelled aircraft names are rejected with suggested database matches. Uppercase/lowercase variants are accepted for manufacturer, model, and variant matching. `type`, `aircraft_type`, `aircraft_model`, and `model_name` are accepted aliases for `model`; `make`, `aircraft_make`, `aircraft_manufacturer`, `manufacturer_name`, and `make_name` are accepted aliases for `manufacturer`; `aircraft_variant` and `variant_name` are accepted aliases for `variant`. Matched aircraft are stored on the certificate as `aircraft_id`, linked to `app.aircraft_models.id`, and used for PDF aircraft details and CO2 calculations. Carbon report only is supported. If no EBC allocation and no positive EBC value is supplied, the API still creates the server record with CO2/flight details, `ebc_purchased_t: 0`, `total_price: 0`, and `carbon_report_only: true`. The record can be found on the server but will not show an EBC value. If EBC allocation is supplied and value fields are omitted, the API calculates value from `offset_percent`, `ebc_purchased_t`, or `ebc_allocated_t` and the company price per tonne. ## Quote ```text POST /api/external/quote Content-Type: application/json ``` Returns a quote without creating a mission or certificate. The quote endpoint accepts the same aircraft, flight, CO2, EBC allocation, currency and supplier commission fields as certificate creation. The API calculates CO2 when it is not supplied, uses the company `ebc_cost_per_tonne` as the default EBC price per tonne, and applies the API user's submitted commission rate or fixed commission amount. Example: ```json { "external_reference": "QUOTE-10428", "aircraft": { "manufacturer": "Gulfstream Aerospace", "model": "G650" }, "flights": [ { "departure": { "iata": "LHR" }, "arrival": { "iata": "NCE" }, "departure_time": "2026-06-03 08:00:00" } ], "ebc_allocated_t": 1.0, "supplier_commission_percent": 10.0 } ``` ## Private Record Mode Set: ```json { "public_status": "private" } ``` Private certificates: - are excluded from the Public Registry list - are excluded from partial public search suggestions - can be opened only when the full certificate number is entered - show aircraft registration as `PRIVATE` on the public certificate and PDF ## Accepted Payload Aliases Legs can be sent as `legs`, `flights`, or `sectors`. Airport codes can be supplied as ICAO or IATA. IATA codes are resolved to the stored ICAO airport record before the mission leg is saved. A-A flights are supported by setting the same departure and arrival airport. A-A flights must include a flight/block duration or both departure and arrival times because the API cannot infer flight time from route distance. A-B flights can calculate flight time from aircraft cruise speed and airport distance. Accepted duration fields: - `duration_hours` - `flight_time_hours` - `block_time_hours` - `duration_minutes` - `flight_time_minutes` - `block_time_minutes` Each leg can use flat fields: ```json { "departure_icao": "EGLL", "arrival_icao": "LFMN", "departure_iata": "LHR", "arrival_iata": "NCE", "off_block_time": "2026-06-03 08:00:00", "on_block_time": "2026-06-03 10:05:00" } ``` or nested fields: ```json { "departure": { "icao": "EGLL", "iata": "LHR", "name": "London Heathrow" }, "arrival": { "icao": "LFMN", "iata": "NCE", "name": "Nice Cote d'Azur" }, "departure_time": "2026-06-03 08:00:00", "arrival_time": "2026-06-03 10:05:00" } ``` Accepted airport code aliases include: - departure: `departure_icao`, `departure_iata`, `from_icao`, `from_iata`, `from`, `origin_icao`, `origin_iata`, `origin`, or nested `departure.icao`, `departure.iata`, `departure.code`. - arrival: `arrival_icao`, `arrival_iata`, `to_icao`, `to_iata`, `to`, `destination_icao`, `destination_iata`, `destination`, or nested `arrival.icao`, `arrival.iata`, `arrival.code`. Aircraft should be identified by: - exact `manufacturer` + `model` + optional `variant` from the aircraft glossary - aliases: `make` for `manufacturer`, and `type` / `aircraft_type` for `model` - custom metrics: `cruise_speed`/`cruise_speed_kt`, `fuel_burn`/`fuel_burn_lph`, `max_range_nm` when agreed for testing or specialist aircraft Set `aircraft` at the top level when one aircraft operates the whole certificate. If different aircraft operate different sectors, add an `aircraft` object to each item in `flights`, `legs`, or `sectors`; the leg aircraft overrides the top-level default for that leg's CO2 calculation. Recommended payload: ```json { "aircraft": { "manufacturer": "Gulfstream Aerospace", "model": "G650", "variant": "G650" } } ``` When `co2_used_t`, `ebc_allocated_t`, and `value_purchased` are supplied, aircraft metrics are optional. The API still stores the aircraft label and flight route details. ## Sandbox The app settings API Credentials popup includes an API Sandbox. Create or reset credentials, paste the one-time client secret into the sandbox, choose Quote, Calculate CO2, or Create Certificate, and run a live JSON payload. ## Database Mapping - `co2_used_t` is stored as `certificates.total_co2_used_t` and `missions.total_co2_t`. - `ebc_allocated_t` is stored as `certificates.ebc_allocated_t`, mirrored to legacy `certificates.total_co2_t`, and used as `missions.offset_co2_t`. - `value_purchased` is converted to USD and stored as `certificates.total_price`. - The submitted value/currency are stored as `certificates.submitted_total_price` and `certificates.submitted_currency`. - FX audit fields are stored as `certificates.fx_rate_to_usd`, `certificates.fx_rate_source`, and `certificates.fx_converted_at`. - Supplier commission is stored as `certificates.supplier_commission_percent`, `certificates.supplier_commission_amount`, `certificates.supplier_commission_currency`, `certificates.submitted_supplier_commission_amount`, and `certificates.submitted_supplier_commission_currency`. - `record_reference` is stored as `certificates.customer_reference`. - `payment_status` is stored as `certificates.invoice_status`. ## Currency Conversion `certificates.total_price` is always stored in USD. When a payload sends a non-USD `currency`, the API fetches a live rate and converts the submitted EBC value to USD. Accepted currency field aliases: - `currency` - `value_currency` - `total_price_currency` - `value_purchased_currency` - `ebc_value_currency` - `certificate_value_currency` Example: ```json { "external_reference": "BOOKING-10428", "co2_used_t": 12.42, "ebc_allocated_t": 10.0, "value_purchased": 2400.0, "currency": "GBP" } ``` Response totals include: ```json { "point_to_point_distance_nm": 558.6, "distance_nm": 558.6, "total_price": 3048.00, "total_price_currency": "USD", "submitted_total_price": 2400.00, "submitted_currency": "GBP", "fx_rate_to_usd": 1.27, "fx_rate_source": "frankfurter", "carbon_report_only": false, "ebc_value_status": "submitted_or_calculated", "supplier_commission_percent": 12.5, "supplier_commission_amount": 381.00, "supplier_commission_currency": "USD" } ``` ## Supplier Commission Supplier commission can be submitted as a percent or a fixed amount. Percent example: ```json { "supplier_commission_percent": 12.5 } ``` Amount example: ```json { "supplier_commission_amount": 300.0, "supplier_commission_currency": "GBP" } ``` Accepted aliases include: - commission percent: `supplier_commission_percent`, `supplier_commission_percentage`, `commission_percent`, `commission_percentage` - commission amount: `supplier_commission`, `supplier_commission_amount`, `supplier_commission_value`, `commission_amount` - commission currency: `supplier_commission_currency`, `commission_currency`, `supplier_commission_value_currency` ## Update Certificate ```text PUT /api/external/certificates/{certificate_number} PATCH /api/external/certificates/{certificate_number} Content-Type: application/json ``` Updates the existing certificate record. You can update customer fields, public/status fields, CO2/EBC amounts, `value_purchased`, `currency`, and, when flight legs are supplied, linked API mission totals. Example update: ```json { "customer_reference": "BOOKING-10428-UPDATED", "co2_used_t": 12.8, "ebc_allocated_t": 10.5, "value_purchased": 2520.0, "currency": "GBP", "payment_status": "paid", "public_status": "public" } ``` ## Record Reference and Payment Status Use `record_reference` for a supplier-side, booking-side, invoice, or external system reference. It is stored against the certificate as a searchable reference. Use `payment_status` to track whether the commercial settlement is complete. New API certificates default to `unpaid`, so AeroVolt can remove, cancel, or void certificates that are not ultimately paid for. Accepted payment statuses: - `unpaid` - `pending` - `paid` - `cancelled` - `void` - `refunded` ## Duplicate Prevention The create endpoint checks these identifiers before inserting: - `external_reference` - `idempotency_key` - `certificate_number` - `record_reference` - `record_id` - `reference` - `booking_reference` - `customer_reference` Default repeat behavior: ```text POST /api/external/certificates/create ``` If a matching certificate already exists, the API returns `200 OK` with `already_exists: true` and does not create another record. Upsert repeat behavior: ```json { "external_reference": "BOOKING-10428", "co2_used_t": 12.8, "ebc_allocated_t": 10.5, "value_purchased": 2520.0, "currency": "GBP", "upsert": true } ``` With `upsert: true` or `update_existing: true`, the matching record is updated instead of duplicated. ## Delete Certificate ```text DELETE /api/external/certificates/{certificate_number} ``` Deletes the certificate for the authenticated API client company. The linked API-created mission is also deleted when it is not linked to any other certificate. Example response: ```json { "ok": true, "deleted": true, "certificate": { "deleted": true, "certificate_id": 123, "certificate_number": "AV-EBC-20260603-1A2B3C4D", "deleted_mission_ids": [456] } } ``` ## Certificate URLs and PDF When `public_status` is `public`, create and detail responses include: - `certificate_url`: browser-ready public certificate page. - `pdf_url`: browser-ready PDF certificate. - `api_url`: authenticated API detail URL. Public certificate page: ```text GET /certificates/{certificate_number} ``` PDF certificate: ```text GET /certificates/{certificate_number}.pdf ``` Authenticated API detail: ```text GET /api/external/certificates/{certificate_number} ``` ## Create Response ```json { "ok": true, "already_exists": false, "created": { "already_exists": false, "certificate_id": 123, "mission_id": 456, "certificate_number": "AV-EBC-20260603-1A2B3C4D", "external_reference": "BOOKING-10428", "public_status": "public", "route_summary": "EGLL-LFMN", "totals": { "point_to_point_distance_nm": 558.6, "distance_nm": 558.6, "fuel_l": 413.2, "kwh": 0, "co2_t": 12.42, "ebc_purchased_t": 10, "offset_fraction": 0.8052, "total_price": 2400, "price_per_tonne": 240 } }, "certificate_url": "https://app.aerovolt.co.uk/certificates/AV-EBC-20260603-1A2B3C4D", "pdf_url": "https://app.aerovolt.co.uk/certificates/AV-EBC-20260603-1A2B3C4D.pdf", "api_url": "https://app.aerovolt.co.uk/api/external/certificates/AV-EBC-20260603-1A2B3C4D" } ``` ## Calculate Only ```text POST /api/external/calculate ``` Use the same payload as create. The response includes the estimate only and does not create records. ## Lookup Created Certificates ```text GET /api/external/certificates/{certificate_number} GET /api/external/certificates/search?q=BOOKING-10428 ``` Public certificate pages: ```text GET /certificates/{certificate_number} GET /certificates/{certificate_number}.pdf ```