Skip to content

Refuelings

Role names and user_group_id values are listed in User groups.

Fuel uplifts, what they cost, and the excise duty inside that cost.

Access: every endpoint below requires user_group_id ≤ 130 (Financial Manager and above) and a non-free company plan. Anything else receives 404. All results are scoped to the caller’s own company.

Derived values are server-side. quantity_l, net_amount, vat_amount, total_amount, excise_amount and status are computed on save and ignored if posted. Do not recompute them in a client: the unit conversion needs the fuel’s density, and two implementations of the same tax arithmetic will eventually disagree.

unit_price is the pump price — excluding VAT, including excise duty. excise_amount is therefore a component of net_amount, never a line added on top of it. net_amount + vat_amount == total_amount always holds.

Worked example — 1,000 l of AVGAS 100LL at €2.10, VAT 21%, Spanish duty €0.40620/l:

Field Derivation Value
quantity_l already litres 1000.000
net_amount unit_price × quantity 2100.00
excise_amount quantity_l × rate 406.20
vat_amount net_amount × vat_rate / 100 441.00
total_amount net_amount + vat_amount 2541.00

The excise is inside the net, so it does not appear in the total. A client that adds excise_amount to total_amount double-counts the duty. The same uplift sent as 720 with unit: "kgs" yields identical figures, because AVGAS is 0.72 kg/l — but only once the aircraft has a fuel_type; without one, quantity_l is null and no duty is derived.


GET /manager/refuelings/index.json

Returns a page of uplifts plus totals and a per-airport rollup. The totals and the rollup cover the whole filtered set, not the current page.

Parameter Type Description
from date YYYY-MM-DD, inclusive
to date YYYY-MM-DD, inclusive
aircraft_id number
airport string Airport ident, exact match
supplier string Exact match
fuel_type string AVGAS100LL, JETA1, JETA, MOGAS, SAF, OTHER
status string PENDING (needs pricing) or PRICED
page number Default 1
limit number Default 25, maximum 1000
{
"refuelings": [
{
"Refueling": {
"id": "8f1c…",
"company_id": "5ef1…",
"aircraft_id": "48",
"flight_id": "0000f43c-…",
"date": "2026-06-01",
"airport": "LELL",
"supplier": "Repsol",
"fuel_type": "AVGAS100LL",
"quantity": "1000.000",
"unit": "l",
"quantity_l": "1000.000",
"currency": "EUR",
"unit_price": "2.1000",
"net_amount": "2100.00",
"excise_amount": "406.20",
"vat_rate": "21.00",
"vat_amount": "441.00",
"total_amount": "2541.00",
"excise_recoverable": "1",
"excise_reclaimed_on": null,
"invoice_ref": "F-2026-0431",
"notes": null,
"status": "PRICED",
"source": "FLIGHT"
},
"Aircraft": { "id": "48", "registration": "EC-KMH" },
"Flight": { "id": "0000f43c-…", "date": "2026-06-01", "departure_airport": "LELL", "landing_airport": "LELL" }
}
],
"pagination": { "page": 1, "current": 25, "count": 412, "pageCount": 17, "limit": 25, "nextPage": true, "prevPage": false },
"totals": [
{ "currency": "EUR", "uplifts": 412, "pending": 30, "litres": 305373.3,
"net": 641283.93, "vat": 134669.62, "excise": 124042.63, "total": 775953.55,
"price_per_litre": 2.1000 }
],
"locations": [
{ "airport": "LELL", "currency": "EUR", "uplifts": 380, "litres": 285039.3,
"net": 598582.53, "excise": 115783.96, "total": 724284.86, "price_per_litre": 2.1000 }
],
"filter": { "from": "2026-01-01", "to": null, "aircraft_id": null, "airport": null, "supplier": null, "fuel_type": null, "status": null }
}

totals and locations carry one entry per currency. Amounts are never converted between currencies — there is no exchange rate source, and a fabricated rate inside a tax figure would be worse than separate subtotals.

quantity_l is null when the quantity cannot be converted: a kgs or lbs uplift on an aircraft with no fuel_type. Such a row gets no automatic duty either. Fix it by setting fuel_type on the aircraft.


GET /manager/refuelings/form_options.json

Aircraft, and the supplier and airport values already in use, for filters and the add form.

{
"options": {
"aircraft": [ { "Aircraft": { "id": "48", "registration": "EC-KMH", "fuel_tracking": "l", "fuel_type": "AVGAS100LL", "active": "1" } } ],
"suppliers": ["Repsol", "Shell"],
"airports": ["LELL", "LEPP"],
"fuel_types": ["AVGAS100LL", "JETA1", "JETA", "MOGAS", "SAF", "OTHER"],
"units": ["l", "usgal", "impgal"],
"uplift_unit": null,
"currency": "EUR"
}
}

POST /manager/refuelings/edit.json

Form-encoded. Include data[Refueling][id] to update; omit it to create.

Field Notes
aircraft_id Required on create. Must belong to your company.
date YYYY-MM-DD. Defaults to today on create.
airport Airport ident
supplier
quantity Required, greater than zero, in the aircraft’s own unit
currency Defaults to the company currency on create
unit_price Ex-VAT, duty-inclusive
vat_rate Percentage, e.g. 21.0
excise_amount Omit to take it from the duty rate table. Rejected if larger than the net amount.
excise_recoverable 1 or 0
excise_reclaimed_on YYYY-MM-DD, for tracking what you have already filed
invoice_ref
notes

fuel_type and unit are not accepted. Both are inherited and snapshotted onto the row when it is created — unit is the company’s uplift unit (uplift_unit above, CompanySetting.fuel_uplift_unit), else the aircraft’s fuel_tracking; fuel_type is the aircraft’s — so that units are stated in exactly one place and changing the company setting or an aircraft later never re-interprets historic quantities.

{ "result": true, "message": "The changes were saved!", "id": "8f1c…", "errors": [] }

POST /manager/refuelings/bulk_price.json

Applies one invoice’s figures to many uplifts at once.

Field Notes
data[Refueling][ids][] Repeat per refuelling. Required.
data[Refueling][unit_price]
data[Refueling][vat_rate]
data[Refueling][supplier]
data[Refueling][invoice_ref]
data[Refueling][currency]

Fields you omit are left unchanged on every row. Each row is saved individually, so litres, VAT, duty and status are derived exactly as on a single edit.

{ "result": true, "message": "38 refuellings updated", "updated": 38 }

POST /manager/refuelings/delete.json

Send data[Refueling][id].

A refuelling seeded from a flight will reappear the next time that flight is saved — the flight still says fuel went in. To remove it permanently, clear the fuel quantity on the flight (and on its legs) instead.

A flight’s refuelling holds every uplift of the flight: Flight.fuel plus the fuel uplifted after each of its legs. It is re-seeded when the flight’s fuel or status changes and when a leg’s uplift is saved or deleted. Its unit is the company’s uplift unit (CompanySetting.fuel_uplift_unit, else the aircraft’s fuel_tracking).


Excise is a fixed amount per litre set by the country of purchase, never a percentage. Rates are effective-dated, and saving one does not restate the duty already stored on past uplifts.

GET /manager/refuelings/rates.json

{
"rates": [
{ "FuelDutyRate": { "id": "a1…", "company_id": "5ef1…", "country": "ES",
"fuel_type": "AVGAS100LL", "amount_per_litre": "0.40620", "currency": "EUR",
"valid_from": "2026-01-01", "valid_to": null } }
]
}

POST /manager/refuelings/rate_edit.json

Field Notes
country ISO code of the airport’s country. Blank = company-wide fallback.
fuel_type Required
amount_per_litre Required, not negative
currency
valid_from Required, YYYY-MM-DD
valid_to Blank = open-ended

Lookup order: a rate for the airport’s own country wins over the company-wide fallback; among equally specific rates, the most recently effective one wins.

POST /manager/refuelings/rate_delete.json

Send data[FuelDutyRate][id].


GET /manager/refuelings/reclaim.json

How much of the excise duty paid in a period is recoverable.

Excise is recoverable by use, not by purchase. The duty each aircraft paid is apportioned by that aircraft’s share of block time flown on flight types where fuel_duty_recoverable = 1:

reclaimable(aircraft) = duty_paid × (qualifying_seconds ÷ total_seconds)

Both sides of the ratio are per aircraft, so the aircraft’s own consumption rate cancels out — no rate weighting is applied or needed.

Only LANDED flights count. A flight with no flight type counts as qualifying, matching the column default: treating unknown as non-qualifying would silently shrink a claim, which is the more damaging of the two mistakes.

Parameter Type Description
from date YYYY-MM-DD, inclusive. Defaults to 1 January of the current year.
to date YYYY-MM-DD, inclusive. Defaults to today.
{
"period": { "from": "2026-01-01", "to": "2026-12-31" },
"aircraft": [
{
"aircraft_id": "8",
"registration": "EC-JMZ",
"currency": "EUR",
"uplifts": 40,
"litres": 1097.9,
"duty_paid": 445.99,
"duty_excluded": 0,
"vat": 504.94,
"qualifying_seconds": 2630520,
"total_seconds": 2630520,
"share": 1,
"reclaimable": 445.99,
"no_hours": false
}
],
"totals": [
{ "currency": "EUR", "duty_paid": 445.99, "duty_excluded": 0,
"vat": 504.94, "reclaimable": 445.99, "litres": 1097.9 }
],
"by_flight_type": [
{ "flight_type_id": "312", "name": "SOLO", "recoverable": true, "flights": 1204, "seconds": 7217640 }
],
"warnings": { "unpriced": 2231, "missing_litres": 0, "missing_duty": 2, "missing_airport": 0 }
}

aircraft is sorted by reclaimable descending, and only includes aircraft that bought fuel in the period.

Field Meaning
duty_excluded Duty on uplifts with excise_recoverable = 0. Reported separately so a deliberate exclusion stays distinguishable from a missing figure.
share qualifying_seconds / total_seconds, or null when the aircraft did not fly.
no_hours The aircraft bought fuel but flew nothing in the period, so nothing is reclaimable against it. reclaimable is 0, never a division by zero.
vat Reference only. Never part of reclaimable — VAT is recovered through a different mechanism entirely.

warnings counts everything that makes the figure understate the real claim. Each entry can only make the reclaim smaller, never larger:

Warning Meaning
unpriced Refuelings still PENDING, so they carry no duty
missing_litres quantity_l is null — a mass uplift on an aircraft with no fuel_type
missing_duty Priced and converted, but no excise_amount — usually no matching duty rate
missing_airport No airport, so no country to match a rate against

GET /manager/refuelings/consumption.json

Two data-quality checks: uplift rate against each aircraft’s own baseline, and fuel bought against fuel billed on.

Parameter Type Description
from date Defaults to 1 January of the current year
to date Defaults to today

The baseline is the twelve months immediately before from, so the period under examination never contributes to the figure it is judged against.

{
"period": { "from": "2026-01-01", "to": "2026-12-31" },
"baseline": { "from": "2025-01-01", "to": "2025-12-31" },
"threshold": 0.25,
"aircraft": [
{ "aircraft_id": "8", "registration": "EC-JMZ", "litres": 12912,
"burned_litres": 11840.2, "measured_hours": 702.4, "source": "measured", "hours": 730.7,
"rate": 16.86, "baseline_rate": 11.62, "baseline_hours": 787.7,
"variance": 0.4510, "flagged": true, "comparable": true }
],
"billing": [
{ "aircraft_id": "8", "registration": "EC-JMZ",
"purchases": [ { "currency": "EUR", "paid": 2354.10, "litres": 1097.9 } ],
"billed": 1980.00, "difference": -374.10, "recovery": 0.8411, "mixed_currency": false }
]
}

litres is always the fuel bought in the period. rate and baseline_rate are litres per block hour of one of two sources, named in source:

  • measured — litres actually burned (burned_litres over measured_hours), from confirmed flights whose fuel used can be worked out from fuel on board (see Flight legs → Fuel), converted to litres with the aircraft’s fuel type. Used only when the aircraft has at least 10 such hours in the period and in the baseline; negative or unconvertible burns are left out.
  • uplift — litres bought over all block hours, as before.

Both sides always use the same source, so a change of method never shows as variance. burned_litres is null when the period has no measured flight.

flagged is |variance| >= threshold, but only for aircraft with at least 10 block hours in the period — below that the ratio is dominated by whether a fill happened to land inside the window. comparable is false when there are too few hours or no baseline.

The threshold of 0.25 was measured against real fleet data rather than picked: at 0.15 it flagged seven aircraft in eleven, which nobody reads.

In billing, difference and recovery are null when mixed_currency is true — there is no exchange rate available to compare purchases across currencies. billed sums user_bills.fuel for flights of that aircraft in the period and has no currency dimension of its own.


POST /manager/refuelings/import.json

JSON body, not form-encoded:

{
"rows": [
{ "aircraft": "EC-KMH", "date": "2026-06-01", "quantity": "120.50",
"airport": "LELL", "supplier": "Repsol", "unit_price": "2.1000",
"vat_rate": "21", "invoice_ref": "F-2026-0431" }
]
}

Rows are expected to be already parsed and column-mapped — supplier statements share no common format, so the mapping belongs where a human can see the file rather than in a server-side guess. Numbers must be normalised to a plain decimal point before sending; 120,50 will be rejected as a bad quantity.

Field Notes
aircraft Registration. Matched ignoring case and separators, so EC-KMH, ECKMH and ec kmh all resolve.
aircraft_id Alternative to aircraft, and takes precedence.
date Anything strtotime understands; stored as Y-m-d.
quantity Required, greater than zero, in the aircraft’s own unit.
airport, supplier, unit_price, vat_rate, excise_amount, currency, invoice_ref Optional

fuel_type and unit are inherited from the aircraft, exactly as on every other creation path. Rows are created with source: "IMPORT".

Maximum 2000 rows per request.

{ "result": true, "imported": 118, "duplicates": 12,
"errors": [ { "line": 7, "reason": "unknown_aircraft", "value": "G-ABCD" } ] }

A row matching an existing refueling on aircraft + date + quantity + invoice reference is counted in duplicates and skipped, so re-importing the same statement never doubles a fuel bill.

reason is one of unknown_aircraft, bad_date, bad_quantity or save_failed. Bad rows are reported with their line number rather than silently dropped; the rest of the import still applies.