Automation Hub Access documentation

Send resource data to Klappir

For customers and partners: SFTP for CSV files, or GraphQL when you prefer an API.

SFTP uploads are processed within 24 hours of file upload. Not sure which to pick? Email service@klappir.com klappir.com Privacy & Terms
Home
Path A:SFTP File Upload
Klappir Developer Docs SFTP File Upload

SFTP File Upload

The no-code path into Klappir. Drop a UTF-8 CSV onto your SFTP folder — Klappir processes uploaded files within 24 hours of file upload — no integration code, no engineering required. Access is SSH key only: you send Klappir your public key, we provision the folder, you connect with your private key.

A
Meet our example throughout this guide

Acme Logistics — a freight and waste-haulage company in Reykjavík with registration number 5501234567. Acme exports diesel deliveries, runs for upstream transportation & distribution, and waste pickups from their Business Central ERP every night, then drops the resulting CSVs onto their Klappir SFTP folder.

In customer scenarios, Acme uploads its own diesel and waste data.In partner scenarios, Acme is a fuel and waste supplier sending data on behalf of three downstream Klappir customers.

Getting started

SSH key setup, connection test, and your first CSV upload.

Your journey

Two stages: connect with your SSH key, then prepare and upload CSVs.

CSV format basics

Encoding, separators, headers, and the conventions every template shares.

Edit / update / delete

Re-upload by Unique ID; zero quantity removes a row.

View and validate

24-hour processing SLA and how to confirm ingestion.

Running in production

Scheduling exports, handling failures, and monitoring uploads.

SFTP versus GraphQL

SFTP is the right choice when your data already lives in spreadsheets, ERP exports (like Business Central), or accounting software. You don't need to write code or maintain a service. The trade-off: it's batch, not real-time — uploaded files are processed within 24 hours of file upload.

If you need real-time submission, programmatic batching, or fine-grained error handling, see the GraphQL API instead.

Getting started #

Once Klappir has installed your SSH public key, you can usually connect and upload within about one business day. Use the checklist for your audience below.

Your path as a Klappir customer
  1. 1
    Email service@klappir.com and request SFTP access.
    Include your company registration number and the SSH public key Klappir should install — paste the full single-line .pub contents (OpenSSH format, e.g. starts with ssh-ed25519 or ssh-rsa). How to generate a key pair is explained in the Get your credentials section. Never send your private key.
  2. 2
    Connect to your SFTP folder
    Klappir replies with your hostname, username, and path. Access is SSH key only (no password). In FileZilla, Cyberduck, WinSCP, or with sftp -i /path/to/private_key …, authenticate using the private key that matches the public key you submitted.
  3. 3
    Look up UNSPSC codes
    Each row needs at least one classification — see Classifications and the UNGM UNSPSC browser.
  4. 4
    Pick and fill the right template
    Seven CSV templates cover fuel, upstream transportation & distribution, waste, three flavours of business travel, and goods & services. Download the Excel file from the template page you need, fill your rows, and save as UTF-8 CSV.
  5. 5
    Upload to your top-level folder
    Put the finished file in the home directory Klappir assigns you (not a subfolder unless Klappir told you otherwise). Klappir processes each file within 24 hours of upload.
Your path as a data partner
  1. 1
    Request partner SFTP access
    Email service@klappir.com with your organisation details and the SSH public key to install (same .pub rules as for customers). Klappir provisions one folder per downstream customer, keyed by their registration number; one login reaches all folders you are authorised for.
  2. 2
    Use the standard templates
    Same seven templates as customers. Every row must carry the correct Legal Entity (UUID) for the customer that row belongs to.
  3. 3
    Upload into each customer's directory
    Save UTF-8 CSVs and place them in the folder Klappir assigns for that customer under your partner root. Exact paths are confirmed when each customer is onboarded.
Connection details are issued per customer

Hostnames and example usernames in this guide are placeholders. The real values for your account are sent to you by email when access is provisioned.

Your journey, in two stages #

Every SFTP integration moves through the same two stages. The sidebar groups operations by stage so you always know where you are.

Stage 1 — Connect

Klappir provisions an SFTP folder for you, keyed by your registration number. You send your SSH public key when requesting access; Klappir installs it and emails you the hostname, username, and path. You always authenticate with the matching private key — passwords are not used.

Stage 2 — Prepare and upload

Pick the CSV template that matches the data you have. Seven templates cover everything Klappir currently accepts — fuel, upstream transportation & distribution, waste, three kinds of business travel, and goods & services. Fill in the columns, save as UTF-8 CSV, and upload the file to your top-level SFTP folder.

Partner — multiple customers, one set of credentials

As a partner, you get one SFTP login that has access to multiple customer folders — one per customer who has authorized your integration. Each folder is keyed by the customer's registration number. New customers appear as new folders within a day of authorizing your integration.

Stage 3 — After upload

After upload, allow up to 24 hours for processing. Then view and validate data in the Klappir platform, use edit / update / delete when corrections are needed, and consult common errors if rows are missing or rejected.

Customer — one folder, your own data

As a direct customer, you have a single folder keyed by your own registration number. Every file you drop is attributed to your organisation automatically.

CSV format basics #

All seven templates share the same conventions. Get these right once and they apply everywhere.

ConventionDetail
File formatComma-separated CSV (.csv). Excel files (.xlsx) are accepted but CSV is preferred — saves a conversion step.
EncodingUTF-8. Critical for non-ASCII characters in addresses, labels, and references (e.g. Icelandic names like Reykjavík).
SeparatorComma (,). Use double quotes around values that contain commas, e.g. "Borgartún 21, 105 Reykjavík".
Header rowFirst row must be the column headers exactly as shown in the template. Don't translate them or change the order.
Required fieldsColumns marked with * in the template header are required. Klappir rejects rows missing required values.
DatesISO 8601 (YYYY-MM-DD) preferred. Excel-style dates (DD/MM/YYYY) are accepted and auto-detected by locale.
DecimalsUse . as the decimal separator (123.45), not comma.
Country codesISO 3166-1 alpha-2, e.g. IS for Iceland, DK for Denmark.
FilenameUse a descriptive name including the date range, e.g. fuel_2024-03.csv. Filenames don't affect routing — Klappir picks the template based on the columns.

Fields you'll see on every template

A handful of columns appear across all seven templates. Understanding them once saves repeating the explanation.

ColumnWhat it means
Unique ID*Your own reference for this row. Used for deduplication — re-uploading a row with the same Unique ID updates the record rather than duplicating it. To delete a row, re-upload it with the same Unique ID and set the quantity (Value) to 0 — see Edit / update / delete. Pick a stable scheme like ACME-FUEL-2024-03-001 and stick with it.
Legal Entity (UUID)*The Klappir customer ID this row belongs to. For customers, use your organisation's UUID from the Klappir Platform: Settings → System → Entity IDs (same value as API customerId).For partners, this is the UUID of whichever downstream customer this row applies to. Each row can target a different customer.
Supplier Label*Name of the supplier or vendor in your records (e.g. "Summit Fuel Supply"). Used for traceability in Klappir reports.
Classification (UNSPSC)*UNSPSC code for the activity — see Classifications for how to choose one.
Asset Label*The site, vehicle, or asset the activity is associated with — e.g. "Truck #14", "Main Office Building", "Wood Piling Road 312".
ReferenceOptional free-text reference such as an invoice number. Helps reconcile back to source documents.
Classifications

Every template needs at least one UNSPSC code per row. Read Classifications before you fill your first CSV — it explains where to look up codes and how many you need per template.

Classifications (UNSPSC) #

Klappir uses UNSPSC codes to tag each row of activity so emissions can be calculated with the right factors. You will see a classification column on almost every CSV template — this section explains what to put there and where to find valid codes.

UNSPSC (United Nations Standard Products and Services Code) is a global taxonomy of products and services. The code you choose tells Klappir what kind of activity the row describes — diesel delivery, mixed waste, a hotel night, and so on — so the platform can apply the correct emission factor.

How many codes per row?

TemplatesClassification column(s)Count
Fuel, upstream transportation & distribution, business travel (all types), goods & servicesClassification (UNSPSC) or Classification Key (UNSPSC)1 per row
WasteWaste type classification (UNSPSC) and Waste treatment classification (UNSPSC)2 per row — what the waste is, and how it was treated
Two codes on waste rows

Waste is the only template that needs two UNSPSC values. The same material can have very different emissions depending on treatment (recycling vs landfill vs incineration). See the Waste template for column details.

Where to find UNSPSC codes

Look up codes on the public UNSPSC browser maintained by UNGM:

https://www.ungm.org/public/unspsc

Use the search and hierarchy to find a code that matches your product, fuel type, travel mode, or waste stream. Copy the code into your CSV using the same hyphenated format returned by getClassifications (e.g. 15-10-15-05-K001 for diesel).

If you later build an API integration, the same taxonomy is available via getClassifications in the GraphQL docs.

Practical rules

  • Match the activity — use a fuel code on fuel rows, a travel-related code on travel templates, and so on. A code from the wrong category is a common rejection reason.
  • Do not invent codes — if Klappir does not recognise a value, the row will fail validation (see Common errors).
  • Stay consistent — once you pick a code for a recurring activity (e.g. diesel for your fleet), reuse it unless the activity type genuinely changes.
Klappir Knowledge Base

How UNSPSC drives emission factors in the platform — hierarchy, fallbacks, and why granularity matters: How the Klappir Platform uses UNSPSC.

A
Acme's codes

Acme caches diesel as 15-10-15-05-K001 and mixed waste keys from the same taxonomy after looking them up on UNGM or via getClassifications. They keep a short internal spreadsheet mapping export categories to keys so finance does not have to search the browser for every row.

1Connect Get your credentials #

Klappir provisions SFTP access on request. Email service@klappir.com with your company registration number and the SSH public key you want installed on the server. Klappir does not use passwords for SFTP — only key-based login. You should get connection details back within about one business day.

SSH keys — what to send Klappir

You keep a key pair: a private key (secret, stays on your machine) and a public key (safe to share). Klappir installs the public key on the SFTP server; you authenticate with the private key in your SFTP client.

  1. If you don't already have a key, generate one on your workstation, for example: ssh-keygen -t ed25519 -f ~/.ssh/klappir_sftp -C "your-company-sftp" (press Enter to skip a passphrase if your policy allows, or set one and enter it each time in the client).
  2. Open the public file (e.g. klappir_sftp.pub). It is a single line starting with ssh-ed25519 or ssh-rsa.
  3. Paste that full line into your email to Klappir, or attach only the .pub file. Never send the private key file (no .pem without .pub, no id_ed25519 without .pub).
  4. Use the matching private key in FileZilla / WinSCP / Cyberduck / sftp -i … when connecting.

Connection details

Hostsftp.automationhub.klappir.io
Port22
ProtocolSFTP (SSH File Transfer Protocol)
AuthSSH public key only (no password)
Usernameyour-registration-number
Home directory/{your-registration-number}/
Before you connect

Host is always sftp.automationhub.klappir.io. Your username and home path are assigned when SFTP is provisioned — wait until Klappir confirms your public key is installed before connecting.

Quick connection test

Once Klappir has confirmed access, verify the connection from your terminal (replace the key path and user with what Klappir sent you):

# Connect with your private key (-i path must be the private file, not .pub)
sftp -i ~/.ssh/klappir_sftp 5501234567@sftp.automationhub.klappir.io

# You should land in your home folder
sftp> pwd
Remote working directory: /5501234567

# List contents — upload production CSVs here (top level of your home)
sftp> ls

# Example upload
sftp> put fuel_2024-03.csv
Uploading fuel_2024-03.csv to /5501234567/fuel_2024-03.csv
FileZilla → Site Manager → New Site

  Protocol:   SFTP - SSH File Transfer Protocol
  Host:       sftp.automationhub.klappir.io
  Port:       22
  Logon type: Key file
  User:       5501234567
  Key file:   <path to your PRIVATE key, e.g. klappir_sftp>

Connect, then drag and drop CSV files into your home folder.

2Templates Fuel #

Liquid and gaseous fuel — diesel, petrol, LPG, marine gas oil, natural gas, biofuels. Use this for fuel consumed by vehicles, generators, boilers, or any combustion equipment. Each row’s Value uses one supported unit: L (litres), kg, (m3), or kWh (kwh in the CSV unit column).

Download: Fuel template (.xlsx)

A
Acme's primary template

Acme uploads fuel_YYYY-MM.csv on the 1st of each month covering the previous month's diesel deliveries to their fleet. A typical month has 200–300 rows split across three downstream customers, distinguished by Legal Entity (UUID).A typical month has 50–80 rows, one per fuel transaction across their truck fleet.

Columns

ColumnRequiredDescription
Unique IDrequiredYour reference for this row, used for deduplication. E.g. ACME-FUEL-2024-03-001
Legal Entity (UUID)requiredUUID of the Klappir customer this row belongs to.
Supplier LabelrequiredName of the fuel supplier (e.g. "Summit Fuel Supply").
DaterequiredWhen the fuel was delivered or consumed. ISO 8601 (2024-03-01) preferred.
ValuerequiredQuantity of fuel as a decimal number, e.g. 450.5
Unit (l/kg/m3/kwh)requiredUnit of the value. One of: l (litres, L), kg, m3 (cubic metres), kwh (kilowatt-hours, kWh).
Fuel descriptionrequiredFree-text description, e.g. "Diesel B7", "Marine Gas Oil"
Customer AccountoptionalYour customer account number with the supplier, if relevant.
Delivery MethodoptionalHow the fuel was delivered, e.g. "Tank truck", "Forecourt"
Address (country)requiredISO country code where fuel was delivered, e.g. IS.
Classification (UNSPSC)requiredUNSPSC classification code for the fuel type.
Asset LabelrequiredThe vehicle, site, or asset that consumed the fuel.
ReferenceoptionalFree-text reference, e.g. invoice number.

Sample rows — Acme's first three records of March

Unique ID* Legal Entity (UUID)* Supplier Label* Date* Value* Unit* Fuel description* Address* Classification* Asset Label* Reference
ACME-FUEL-2024-03-001afaf2111-f5cc-...28b5dSummit Fuel Supply2024-03-01450.5lDiesel B7IS15-10-15-05-K001Truck #14Invoice 4521
ACME-FUEL-2024-03-002afaf2111-f5cc-...28b5dSummit Fuel Supply2024-03-02312.0lDiesel B7IS15-10-15-05-K001Truck #08Invoice 4522
ACME-FUEL-2024-03-003afaf2111-f5cc-...28b5dHarbor Energy Co.2024-03-03189.7lDiesel B7IS15-10-15-05-K001Truck #14Invoice 8841

2Templates Upstream transportation & distribution #

Upstream transportation & distribution — inbound, outbound, and internal logistics. Used by logistics providers, shipping companies, and manufacturers tracking Scope 3 supply chain emissions. Captures origin and destination countries, distance in kilometres, weight in kg, and whether the move is inbound, outbound, or internal from the customer’s perspective.

Download: Upstream transportation & distribution template (.xlsx)

Columns

ColumnRequiredDescription
Unique IDrequiredYour row reference.
Legal Entity (UUID)requiredKlappir customer UUID.
Supplier LabelrequiredCarrier or freight forwarder name.
Date (of shipment)requiredWhen the shipment occurred.
Load Address (country code)requiredISO country code of pickup point.
Unload Address (country code)requiredISO country code of destination.
Shipping distance (km)requiredDistance travelled, in kilometres.
Weight (kg)requiredMass in kilograms for this upstream transportation & distribution record.
Transport Type (inbound/outbound/internal)requiredinbound, outbound, or internal from the customer’s perspective (internal = moves within the organisation’s own sites).
Data LabelrequiredDescription or label for this shipment.
Asset LabelrequiredSite or vehicle reference.
Classification (UNSPSC)requiredUNSPSC code for the upstream transportation & distribution category.
ReferenceoptionalBill of lading or invoice reference.

2Templates Waste #

Waste collected from the customer's operations. Used by waste haulers and environmental service companies, and by direct customers reporting their own waste streams. Records weight, pickup location, plus separate UNSPSC codes for the type of waste and the treatment method (recycling, landfill, incineration, etc.).

Download: Waste template (.xlsx)

Columns

ColumnRequiredDescription
Unique IDrequiredYour row reference.
Legal Entity (UUID)requiredKlappir customer UUID.
Supplier LabelrequiredWaste hauler or treatment provider name.
DaterequiredPickup date.
ValuerequiredQuantity of waste as a decimal.
Unit (kg)requiredUse kg (mass in kilograms).
Product LabelrequiredFree-text waste description, e.g. "Mixed commercial waste".
Pickup address (country)requiredISO country code of pickup site.
Pickup locationoptionalSpecific street address or site label.
Waste type classification (UNSPSC)requiredUNSPSC code for the type of waste.
Waste treatment classification (UNSPSC)requiredUNSPSC code for the treatment method (recycling, landfill, incineration, composting, etc.).
Asset LabelrequiredSite reference.
ReferenceoptionalPickup ticket or invoice reference.
Two classification codes per row

Waste records carry two UNSPSC codes — one for what the waste is (e.g. mixed paper, food waste, hazardous chemicals) and one for how it's treated (recycled, landfilled, incinerated). Both drive the emissions calculation — the same waste type has very different emissions depending on treatment. See Classifications for where to look up codes on UNGM.

2Templates Business Travel — Flight #

Air travel for business purposes. Used by travel agencies, corporate travel management companies, and organisations self-reporting employee flights. Origin and destination airports use IATA three-letter codes (e.g. KEF, CPH, JFK).

Download: Business Travel — Flight template (.xlsx)

Columns

ColumnRequiredDescription
Unique IDrequiredYour row reference.
Legal Entity (UUID)requiredKlappir customer UUID.
Supplier LabelrequiredAirline or travel agency name.
Date (of travel)requiredDate of the flight.
From Airport (IATA code)requiredOrigin airport, IATA code (e.g. KEF).
To Airport (IATA code)requiredDestination airport, IATA code.
Passengers (count)requiredNumber of passengers on the booking.
Travel DescriptionrequiredFree-text description, e.g. "Annual conference attendance".
Classification (UNSPSC)requiredUNSPSC code reflecting cabin class.
Asset LabelrequiredDepartment, project, or cost-centre label.
ReferenceoptionalPNR or booking reference.

2Templates Business Travel — Surface #

Non-aerial business travel — road, rail, ferry, and sea. Use for taxis, rental cars, employee mileage, trains, ferries, and any combination thereof. Captures origin and destination countries plus distance in kilometres.

Download: Business Travel — Surface template (.xlsx)

Columns

ColumnRequiredDescription
Unique IDrequiredYour row reference.
Legal Entity (UUID)requiredKlappir customer UUID.
Supplier LabelrequiredTaxi company, car rental, rail operator, ferry company, or Employee for mileage claims.
Date (of travel)requiredDate of the journey.
From Country (country code)requiredISO country code of origin.
To Country (country code)requiredISO country code of destination.
Distance (km)requiredTotal distance, kilometres.
Passenger countrequiredNumber of passengers.
Travel DescriptionrequiredFree-text description.
Classification (UNSPSC)requiredUNSPSC code for transport mode.
Asset LabelrequiredDepartment or cost-centre.
ReferenceoptionalBooking or expense reference.

2Templates Business Travel — Hotel #

Hotel stays for business purposes. Used by travel agencies and organisations tracking Scope 3 business travel. Country drives the emission factor (hotels in different countries have very different per-night carbon footprints depending on the local energy grid).

Download: Business Travel — Hotel stays template (.xlsx)

Columns

ColumnRequiredDescription
Unique IDrequiredYour row reference.
Legal Entity (UUID)requiredKlappir customer UUID.
Check in daterequiredDate of check-in.
CountryrequiredISO country code where the hotel is located.
Number of nightsrequiredTotal nights stayed.
Number of roomsrequiredRooms booked.
Number of guestsrequiredNumber of guests across all rooms.
Stay descriptionrequiredFree-text description, e.g. hotel name.
Classification (UNSPSC)requiredUNSPSC code for accommodation type.
Asset LabelrequiredDepartment or cost-centre.
ReferenceoptionalBooking reference.
Note: Supplier Label not used here

Unlike most templates, the hotel template does not have a separate Supplier Label column — put the hotel name in Stay description instead.

2Templates Goods & Services #

Purchased goods and services — Scope 3 Category 1 reporting. Each row represents a procurement record. The most flexible template: you can supply quantity (kg) plus a known emission factor (kgco2e), or you can supply just the quantity and let Klappir apply a default emission factor based on the UNSPSC classification.

Download: Goods & services template (.xlsx)

A
From the actual Klappir template

The Goods & Services template ships with two example rows out of the box: a wood piling purchase from "ACME Suppliers Inc" (UNSPSC 30102804) and an apple purchase (UNSPSC 50301500). The template uses these to show how the same supplier appears across very different product categories.

Columns

ColumnRequiredDescription
Unique IDrequiredYour row reference.
Legal Entity (UUID)requiredKlappir customer UUID.
Supplier LabelrequiredVendor name.
DaterequiredPurchase date.
Unit (kg)required (one of)Unit of the value, currently kg.
Valuerequired (one of)Quantity purchased.
kgco2erequired (one of)Direct CO₂e emissions for this purchase, if you have a verified figure from the supplier.
kgco2e Source (if applicable)optionalReference for the kgco2e value (EPD, supplier statement, etc.) when supplied.
Product LabelrequiredFree-text product name, e.g. "Wooden piling".
Classification Key (UNSPSC)requiredUNSPSC code for the product or service category.
Asset LabelrequiredSite or project the purchase is associated with.
ReferenceoptionalPO or invoice number.
Quantity-based or emission-based — supply at least one

Marked with ** in the original template, you must supply either quantity (Unit + Value) or a direct emission figure (kgco2e). If you supply both, Klappir uses your kgco2e value as authoritative and stores the quantity for traceability.

Sample rows — straight from the Klappir template

Unique ID* Legal Entity (UUID)* Supplier Label* Date* Unit* Value* kgco2e* Product Label* Classification* Asset Label*
67ef84af80a823163150-3244-...80a8ACME Suppliers Inc2026-01-15kg420.0Wooden piling30102804Wood Piling Road 312
162843c10333acde070d-8c4c-...0333ACME Suppliers Inc2026-02-07kg12.0Apples50301500Main Office Building

3After upload Edit, update & delete data #

SFTP is your correction channel. You do not edit rows in place on the server — you change the CSV and upload again. Klappir matches each row by Unique ID (the same identifier as externalId in the GraphQL API). Understanding updates and deletions upfront saves painful rework later.

Processing time (SLA)

Klappir processes uploaded files within 24 hours of file upload. After that window, accepted rows appear in the Klappir platform for the organisation and period you expect. Plan spot-checks (see View and validate data) after each upload, not only on the first run.

Update an existing row

To change a record that already ingested successfully:

  1. Keep the same Unique ID as the original row.
  2. Change any other columns that need correcting (date, quantity, classification, supplier, and so on).
  3. Upload a CSV that includes the corrected row — it can be the full export again or a small file with only the changed rows, as long as headers match the template.

Klappir treats the new file as an update for that Unique ID, not a second copy. This is the same deduplication behaviour described in CSV format basics.

GraphQL equivalent

Re-submit a mutation record with the same externalId to update it, or set quantity fields to zero to delete. See Edit / update / delete in the GraphQL docs.

Delete a row

To remove a record from Klappir, upload a row with the same Unique ID and set the quantity column to zero:

  • Fuel, upstream transportation & distribution, waste, goods & services — set Value to 0.
  • Business travel templates — set the primary quantity field to 0 (for example distance, nights, or passenger count as defined in that template).

Do not delete the row from your CSV and expect Klappir to infer removal — absence from a new file does not delete prior data. The zero-quantity row is the deletion signal.

Recommended correction workflow

StepAction
1Confirm the original row ingested — View and validate data.
2Fix the source export or build a minimal correction CSV with the same template headers.
3Re-upload; wait up to 24 hours for processing.
4Validate again in the platform. If the row still looks wrong, check Common errors or contact service@klappir.com.
A
Acme fixes a typo

Acme discovers ACME-FUEL-2024-03-002 used the wrong date. They change one cell, re-upload fuel_2024-03-correction.csv with that single row (same headers as the Fuel template), and confirm in the platform the next day that March activity shows the corrected date.

3After upload View and validate data #

Uploading is only half the job. Use the Klappir platform to confirm that rows were accepted and appear under the right legal entity and time range. Pair that with the error patterns in this guide when something does not show up as expected.

When to check

Allow up to 24 hours after file upload for Klappir to process the file. If nothing changes after that, treat it as an ingestion or validation issue rather than normal delay.

What to verify in the Klappir platform

  • Organisation — activity is attributed to the legal entity matching Legal Entity (UUID) in the CSV (for customers, your own UUID; for partners, each downstream customer).
  • Time period — dates on rows map to the reporting period you expect (monthly uploads should appear in that month).
  • Volume — row counts and totals are in the right ballpark compared to your source system (fuel litres, waste kg, travel segments, and so on).
  • Classification — categories line up with the UNSPSC keys you submitted; unexpected gaps often mean rejected rows.

Interpreting validation outcomes

What you seeLikely meaningWhat to do
Data appears as expected within 24 hoursFile and rows acceptedNo action — continue your regular export schedule.
Partial data — some periods or sites missingSome rows failed validation while others succeededCompare against your CSV; fix rejected rows and re-upload. See Common errors.
Nothing new after 24 hoursFile-level rejection, wrong folder, or transfer never completedCheck SFTP client logs, filename, and template headers. Re-upload after fixing.
Duplicates or doubled totalsChanged Unique ID scheme or re-sent overlapping exportsStabilise IDs; use Edit / update / delete to correct instead of inventing new IDs for the same activity.
Garbled characters in labelsEncoding issueRe-save as UTF-8 CSV and upload again.
Row-level vs file-level failures

Row-level problems (bad date, unknown UNSPSC, wrong unit) usually affect only the offending rows — the rest of the file can still process. File-level problems (wrong headers, empty file, corrupt encoding) reject the entire upload. The Common errors section lists both.

3After upload Common errors #

Most rejection reasons fall into a small handful of categories. Once you've seen each once, you'll fix them in seconds. When the pipeline rejects rows or a whole file, the reason is usually tied to one of the patterns below — match your symptom, apply the fix, and re-upload.

The frequent flyers

ErrorCauseFix
Header row not recognisedColumn names don't match any template, often because of an extra space or different capitalisation.Re-download the official template from the matching template page and copy your data into it. Don't modify the header row.
Required field missingA column marked * is blank.Either fill the cell or delete the row entirely.
Invalid country codeSpelled out ("Iceland") or three-letter ("ISL") instead of ISO alpha-2 ("IS").Use ISO 3166-1 alpha-2: 2-letter codes only. IS, DK, NO, US.
Date couldn't be parsedMixed format (03/01/24), Excel serial number, or an impossible date like 2024-13-05.Use ISO 8601: 2024-03-01. If you must use a national format, be consistent across the whole file.
Decimal separatorEuropean comma decimals (123,45) instead of period (123.45).Configure your CSV export to use period as the decimal separator. In Excel, this is the regional setting.
Unknown UNSPSC codeCode doesn't exist or has been retired in the current UNSPSC version.Look up a current code on UNGM UNSPSC, or use getClassifications if you integrate via API. Your account manager can help during onboarding.
UUID doesn't match an authorized customerThe Legal Entity (UUID) column contains an ID that isn't yours, or — for partners — a customer that hasn't authorized your integration.For customers: confirm you're using the UUID Klappir gave you during onboarding. For partners: the customer needs to enable your integration in their Klappir account first.
File encodingSpecial characters (Icelandic, Nordic, accented) appear garbled.Save as UTF-8. In Excel: File → Save As → CSV UTF-8 (Comma delimited). Avoid the plain "CSV" option which uses Windows-1252.
Comma inside a quoted valueAddress contains a comma but isn't quoted.Wrap any value containing a comma in double quotes: "Borgartún 21, 105 Reykjavík".
Unit not in allowed listUsed a unit not listed in the template, e.g. gallons in the Fuel template.Convert to one of the allowed units before upload. The Fuel template accepts l, kg, m3, or kwh only (L, kg, m³, kWh).

If a whole file is rejected

A small number of conditions cause Klappir to reject the entire file rather than process row by row:

  • The file isn't a recognised CSV (binary content, wrong extension, corrupted).
  • The header row doesn't match any of the seven templates.
  • The file is empty or has no data rows after the header.
  • The file is implausibly large — uploads over 50,000 rows are flagged for Klappir review before processing.

In all of these cases Klappir rejects the file at ingest time with a clear file-level reason (or Klappir can confirm what happened). Fix and re-upload — there's nothing to clean up on Klappir's side.

Running in production #

Once your first upload works end-to-end, the next step is making this part of your routine without having to think about it.

Schedule the export

Most customers run their CSV export on a fixed cadence — daily, weekly, or monthly. Use cron, Windows Task Scheduler, or your ERP's built-in scheduler to drop files onto the SFTP server automatically.

Pick a stable Unique ID scheme

Acme uses ACME-FUEL-2024-03-001 — prefix, type, year-month, sequential. Once you commit to a scheme, don't change it. Future you will thank you when reconciling historical data.

Confirm data in Klappir

After each upload, allow up to 24 hours for processing, then spot-check in the Klappir platform that new activity appears as you expect for the right organisation and time period. See View and validate data.

Keep SFTP client and scheduler logs

Your SFTP client or automation logs show whether a transfer completed. If a file never left your side, the issue is local (path, key, network) before it reaches Klappir.

Keep originals locally

Klappir archives processed files internally for traceability, but you should keep your own copies of every file you upload. They're your audit trail for the export, separate from Klappir's record of ingestion.

Refresh the UNSPSC list

The classification taxonomy expands over time. If you're hard-coding codes in your export, revisit them every 6–12 months using the UNGM UNSPSC browser or your Klappir account manager.

When something goes wrong

Email service@klappir.com with the timestamp of the upload attempt, the filename, what you see in the Klappir platform (if anything), and any error text from your SFTP client or scheduler logs. The Klappir team can usually help within a business day.

Want to switch to GraphQL later?

If you grow into a use case that needs real-time submission, programmatic batching, or fine-grained error handling, the GraphQL API is the next step up. Same data, same classifications, same customer model — different transport.

Home
Path B:GraphQL API
Klappir Developer Docs GraphQL API

Klappir GraphQL API Reference

A single GraphQL endpoint for submitting data into the Klappir platform. The same data types and field concepts as the SFTP CSV templates — see SFTP and API field alignment.

A
Meet our example throughout this guide

Acme Logistics — a freight and waste-haulage company in Reykjavík with registration number 5501234567. Every code example you see uses Acme's setup: customerId: afaf2111-f5cc-4c69-b806-46d318e28b5d, classificationKey 15-10-15-05-K001 (diesel), and externalIds prefixed ACME-.

Customer scenarios show Acme uploading its own diesel and waste data.Partner scenarios show Acme submitting upstream transportation & distribution and fuel data on behalf of its three downstream customers.

API Endpoint & Authentication

Endpoint: https://api.klappir.com/graphql

Required headers on every request:

{
  "Content-Type": "application/json",
  "x-api-key": "your-api-key-here"
}

No API key yet? Email service@klappir.com to request one. If you'd rather avoid building an integration entirely, see the SFTP path.

Your path as aKlappir customer
  1. 1
    Get an API key from Klappir
    Email service@klappir.com to request a key. You pass it on every request as the x-api-key header (details in the Getting started section).
  2. 2
    Find your organisation UUID
    In the Klappir Platform, open Settings → System → Entity IDs — the value you can see is your customerId on every mutation.
  3. 3
    Look up classification keys
    Fetch the getClassifications codes that match the activity types you'll be sending.
  4. 4
    Submit your activity records
    Use the create* mutation matching your data — fuel, waste, business travel, upstream transportation & distribution, goods and services.
  5. 5
    Correct or remove rows when needed
    Re-submit with the same externalId to update; set quantity fields to 0 to delete — see Edit / update / delete.
Your path as adata partner
  1. 1
    Get an API key from Klappir
    Email service@klappir.com to request a key. You pass it on every request as the x-api-key header (details in the Getting started section).
  2. 2
    Discover authorized customers
    Call getActiveCustomers to see which Klappir customers have enabled your integration. Each id in the response is the customerId for that customer's records. Refresh regularly.
  3. 3
    Look up classification keys
    Fetch the getClassifications codes that match the activity types you'll be sending.
  4. 4
    Submit your activity records
    Use the create* mutation matching your data — fuel, waste, business travel, upstream transportation & distribution, goods and services. Each record carries a customerId, so one batch can cover many customers at once.
  5. 5
    Correct or remove rows when needed
    Re-submit with the same externalId to update; set quantity fields to 0 to delete — see Edit / update / delete.

Getting started #

The Klappir Public API is a single GraphQL endpoint. All requests use HTTP POST with Content-Type: application/json. There are two operation types:

query

Read data from Klappir — for example classification keys and other reference data you need before submitting records about your own organisation.Read data from Klappir — customers you're authorized to send data for, valid classifications, and related metadata.

mutation

Write data to Klappir — submit fuel, waste, travel, upstream transportation & distribution, and other sustainability records.

Every request body is a JSON object with two keys:

KeyRequiredDescription
queryrequiredA string containing the GraphQL operation.
variablesoptionalA JSON object with input parameters referenced inside the query string.

In GraphQL, ! marks a field as non-null (required). For example, CreateFuelInput! means the input argument must be provided.

The examples below walk through a first successful read for your audience. If a request fails before that works, jump to Troubleshooting common API errors at the end of this section.

Acme's first call

Acme Logistics already has customerId from the Klappir Platform (Settings → System → Entity IDs). Before the first fuel mutation they fetch valid classification keys:

{
  "query": "query { getClassifications(input: { dataType: [FUEL] }) { key level dataType usage description } }"
}
{
  "data": {
    "getClassifications": [
      {
        "key": "15-10-15-05-K001",
        "level": 5,
        "dataType": "FUEL",
        "usage": "General classification",
        "description": "Diesel 100% mineral"
      },
      {
        "key": "15-10-15-06-K001",
        "level": 5,
        "dataType": "FUEL",
        "usage": "General classification",
        "description": "Petrol 100% mineral"
      }
    ]
  }
}

They pick a key from the response for classificationKey on each record. Full options and filters: getClassifications. Every mutation example in this guide uses customerId: afaf2111-f5cc-4c69-b806-46d318e28b5d — the same UUID Acme sees in the Platform.

Acme's first call

Acme integrates as a partner. Their first read lists Klappir customers that have authorized their integration:

{
  "query": "query { getActiveCustomers { id, name, registration } }"
}
{
  "data": {
    "getActiveCustomers": [
      {
        "id": "afaf2111-f5cc-4c69-b806-46d318e28b5d",  // downstream customer id
        "name": "Acme Logistics",
        "registration": "5501234567"
      }
    ]
  }
}

Each id is the customerId to set on records for that customer. Acme caches the list and refreshes it on a schedule. Partner flows also use getClassifications before submitting.

Troubleshooting common API errors

If a request fails, use this table to diagnose quickly and apply the exact fix.

What you seeLikely causeExact fix
401 UnauthorizedMissing or invalid x-api-key header.Send both headers on every request: Content-Type: application/json and x-api-key: <your-key>. If it still fails, request a new or rotated key from service@klappir.com.
GraphQL validation error (for example: required field missing, wrong type)Payload does not match mutation input type (for example missing time, string passed where number is required).Check the mutation's input type and ensure every required field is present with correct type. Start from the doc's example payload, then add fields incrementally.
customerId missing / null / unauthorized-customer style errorRecord has no customerId, or ID is not currently authorized for your integration.Use the UUID from Platform Settings → System → Entity IDs (must match your organisation).Fetch getActiveCustomers, pick an id from that result, and set it on every record. Refresh this list regularly before submission.
Invalid or unknown classificationKeyClassification key is outdated, misspelled, or for the wrong data type.Call getClassifications and select a current key for your data type. Do not hard-code old values; refresh your classification cache on a schedule.

Your journey, in two stages #

Every Klappir API integration moves through the same two stages, in order. The sidebar groups operations by stage so you always know where you are.

Stage 1 — Discover

Your customerId is your organisation UUID in the Klappir Platform: Settings → System → Entity IDs. Use getClassifications for valid classification keys on each record.

Find out who you're sending data for and how that data should be classified. getActiveCustomers tells you which customers you're authorized to submit data for. getClassifications tells you the valid codes for tagging each record.

Stage 2 — Submit data

Pick the create* mutation that matches your data type and submit records in batches. Every record carries your organisation's customerId.

Pick the create* mutation that matches your data type and submit records in batches. Each record carries a customerId for the customer it belongs to, so one batch can cover many customers at once. You'll come back to this stage continuously as new data flows in.

Partner — authorization first

The API only accepts data for customers who have explicitly granted your integration access. getActiveCustomers returns only currently authorized customers. If a customer isn't in the list, you can't send data for them yet.

Customer — your own ID

As a direct customer, copy your organisation UUID from the Klappir Platform: Settings → System → Entity IDs — that's your customerId in every mutation.

Sending data, in concept #

Before diving into specific mutations, three fields appear on nearly every record. Understanding them once will save you a lot of confusion later.

In the GraphQL sections below, field tables include a Req column: required (green) means non-null in the schema or a mandatory nested input; optional (gray) means you may omit the field. Nullable quantity fields can still require at least one value per row — see each mutation’s note above its table.

ReqFieldTypeDescription
requiredcustomerIdString!Your organisation's UUID from the Klappir Platform: Settings → System → Entity IDs.The id from getActiveCustomers for the customer this row belongs to. Routes the record to the correct Klappir account.
requiredclassificationKeyString!A UNSPSC-based key from getClassifications. Determines how the record is categorized in emissions calculations.
requiredtimeDateTime!ISO 8601 timestamp for when the activity occurred. Always UTC.
optionalexternalIdStringYour own reference ID (required on each mutation input row). Same role as Unique ID on SFTP — re-submitting with the same externalId updates the record; zero quantity removes it. See Edit / update / delete.

Classifications, briefly

Klappir uses a classification system built on UNSPSC codes. Each record needs one. Call getClassifications without input for all keys your integration supports, or pass dataType: ["FUEL", …] (an array) to narrow.

Acme's lookup

Acme deals primarily in diesel and mixed waste. They cache key values from getClassifications on first run (e.g. diesel under dataType: ["FUEL"]). They refresh the cache weekly.

Always fetch the latest list from getClassifications rather than hard-coding — the taxonomy expands regularly.

externalId — your deduplication anchor

The externalId is your record's identity from your system's perspective — the API equivalent of SFTP Unique ID. Acme uses a scheme like ACME-FUEL-2024-03-001 (prefix, type, year-month, sequential). Pick a scheme and stick with it. Corrections and deletions use the same rules as the SFTP path; see Edit / update / delete.

Mutation quick reference

Seven create-style mutations, one per data category. Use the one that matches what you have:

MutationData typeKey quantity field(s)
createFuelFuel consumptionAny combination of liters (L), kg, m3, kwh per row
createCargoUpstream transportation & distributiontransportType, date, addresses, distance, weight
createWasteWaste disposalkg
createFlightTravelAir travelorigin, destination, cabin class, passengers
createSurfaceTravelRoad / rail / sea traveldistanceTraveled, addresses, passengers
createHotelTravelHotel staysnights, country
createGoodsServicesPurchased goods & servicesproductLabel, classificationKey, at least one of kg / kgco2e

SFTP and API field alignment #

The GraphQL API and SFTP CSV templates describe the same data. Data types, classification keys, customer routing, and deduplication rules align — only the transport differs (HTTP mutations vs file upload). If you know one path, you can map directly to the other.

Shared concepts

ConceptSFTP (CSV)GraphQL API
Customer routingLegal Entity (UUID)customerId
Row identity / deduplicationUnique IDexternalId
ClassificationClassification (UNSPSC) or template-specific UNSPSC columnsclassificationKey (and wasteClassificationKey / treatmentClassificationKey for waste)
Activity dateDate (ISO 8601)time (DateTime!, UTC)
Update existing rowRe-upload same Unique IDRe-submit same externalId
Delete rowSame Unique ID, quantity 0Same externalId, zero quantity fields
Processing SLA (batch)Within 24 hours of file uploadMutations are processed on request; responses are immediate

Data type ↔ mutation ↔ template

Data typeSFTP templateGraphQL mutation
FuelFuelcreateFuel
Upstream transportation & distributionUpstream transportation & distributioncreateCargo
WasteWastecreateWaste
Business travel — flightBusiness Travel — FlightcreateFlightTravel
Business travel — surfaceBusiness Travel — SurfacecreateSurfaceTravel
Business travel — hotelBusiness Travel — HotelcreateHotelTravel
Goods & servicesGoods & ServicescreateGoodsServices

Updates and deletions follow the same rules on both paths — see Edit / update / delete (API) and Edit / update / delete (SFTP). Classifications come from the same UNSPSC taxonomy — SFTP: Classifications, getClassifications in the API. See also the Klappir Knowledge Base article on UNSPSC.

1Discover QUERY getActiveCustomers #

List Klappir customers your integration may send data for. This query returns every customer that has enabled your integration. Refresh regularly — authorization can change overnight.

A
Acme as partner

Acme calls this nightly. Today it returns three customers — Reykjavík Hospital, Northwind Brewery, and Lighthouse Studios. Tomorrow it might return four if a new customer enabled the integration overnight.

QUERYGet active customersgetActiveCustomers

Returns [Customer!]! — no arguments required

Query

query getActiveCustomers {
  getActiveCustomers {
    id
    name
    registration
    description
    address { country address city postalCode }
  }
}

Response — Acme's view

{
  "data": {
    "getActiveCustomers": [
      {
        "id": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
        "name": "Acme Logistics",
        "registration": "5501234567",
        "description": null,
        "address": {
          "country": "IS",
          "address": "Borgartún 21",
          "city": "Reykjavík",
          "postalCode": "105"
        }
      }
    ]
  }
}

1Discover QUERY getClassifications #

Fetch valid classification keys for tagging your data records. Every record you submit needs a classificationKey. This query returns the available codes.

A
Acme's lookup

Acme filters with dataType: [FUEL] on first run, finds the diesel key (e.g. 15-10-15-05-K001) and caches it. Their nightly job refreshes the cache once a week.

QUERYClassificationsgetClassifications

Returns [Classification!]!

Arguments

ReqArgumentTypeDescription
optionalinputGetClassificationsInputOptional. Filter by dataType — an array of DataTypeEnum values (e.g. [FUEL]) — and/or keyPrefix string. Omit input to return classifications for all data types your integration supports.

Query

query getClassifications($input: GetClassificationsInput) {
  getClassifications(input: $input) {
    key
    level
    dataType
    usage
    description
  }
}

Response fields (Classification)

ReqFieldTypeDescription
requiredkeyID!Klappir classification ID — copy verbatim into classificationKey (or waste keys) on mutations. Hyphen-separated hierarchy (e.g. 15-10-15-05-K001), not compact UNSPSC digits.
requiredlevelInt!Depth in the classification hierarchy (higher = more specific).
requireddataTypeDataTypeEnum!Which mutation family this key applies to (e.g. FUEL, WASTE).
requiredusageString!How Klappir expects the key to be used (internal label).
requireddescriptionString!Human-readable label for integrators.

Variables — Acme filtering for fuel

{ "input": { "dataType": ["FUEL"] } }

dataType is a list in the schema — use an array even for one type, e.g. ["FUEL", "WASTE"] to prefetch keys for multiple templates.

Response

{
  "data": {
    "getClassifications": [
      {
        "key": "15-10-15-05-K001",
        "level": 5,
        "dataType": "FUEL",
        "usage": "General classification",
        "description": "Diesel 100% mineral"
      },
      {
        "key": "15-10-15-06-K001",
        "level": 5,
        "dataType": "FUEL",
        "usage": "General classification",
        "description": "Petrol 100% mineral"
      }
    ]
  }
}

Sample rows (truncated). Your environment returns the full list — always copy key values from your own response into classificationKey.

2Submit MUTATION createFuel #

Submit fuel consumption records. Use this for any liquid or gaseous fuel — petrol, diesel, marine gas oil, natural gas, LPG, biofuels — consumed by vehicles, generators, boilers, or other equipment.

A
Acme's flagship mutation

Acme runs createFuel nightly with the previous day's diesel deliveries. Each batch contains records for all three of their downstream customers, distinguished by customerId on each record.A typical nightly batch is 50–200 records, one per fuel transaction across their fleet.

MUTATIONFuelcreateFuel

Returns CreateDataPayload!

Input: CreateFuelInput! → data: [CreateSingleFuelInput!]!

You can submit one or many fuel records in data. For each record you may set one or several quantity fields among liters (litres, L), kg (kilograms), m3 (cubic metres, ), and kwh (kilowatt-hours, kWh) — for example both liters and kg when your data includes both.

ReqFieldTypeDescription
requiredexternalIdString!Your unique row ID (deduplication)
requiredtimeDateTime!When the fuel was consumed (UTC)
requiredcustomerIdString!Target Klappir customer ID
requiredclassificationKeyString!UNSPSC key from getClassifications
requiredassetDescriptionString!Asset tied to the activity (vehicle, site, etc.). Min. length enforced by API.
requiredfuelDescriptionString!Fuel product (e.g. Diesel B7). Min. length enforced by API.
optionalsupplierLabelStringSupplier display name
optionaldeliveryPointStringDelivery point or supplier reference (truck reg., station id, vessel IMO, etc.)
requiredaddressAddressInput!Station or delivery address (see AddressInput)
optionallitersFloatVolume in litres (L)
optionalkgFloatMass in kilograms
optionalm3FloatVolume in cubic metres ()
optionalkwhFloatEnergy in kilowatt-hours (kWh)
optionalreferenceStringInvoice or internal reference

Mutation

mutation createFuel($input: CreateFuelInput!) {
  createFuel(input: $input) {
    success
    message
    rows
  }
}

Variables — Acme submitting one diesel record

{
  "input": {
    "data": [{
      "externalId": "ACME-FUEL-2024-03-001",
      "time": "2024-03-01T08:00:00.000Z",
      "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
      "classificationKey": "15-10-15-05-K001",
      "assetDescription": "Truck #14",
      "fuelDescription": "Diesel B7",
      "address": {
        "country": "IS",
        "city": "Reykjavík",
        "address": "12 Harbor Lane",
        "postalCode": "105"
      },
      "liters": 450.5,
      "kg": 382.0,
      "reference": "Invoice #4521"
    }]
  }
}

Response

{"data": {"createFuel": {"success": true, "message": "OK", "rows": 1}}}

2Submit MUTATION createCargo #

Submit upstream transportation & distribution records. Use for inbound, outbound, or internal logistics moves by road, sea, rail, or air. In the API this mutation is still named createCargo; input is data, an array of CreateSingleCargoInput.

A
Upstream transportation & distribution

For upstream transportation & distribution, Acme tags every move as OUTBOUND from their depot or INBOUND when receiving stock from suppliers. Their fleet management system already has this distinction.

MUTATIONUpstream transportation & distributioncreateCargo

Returns CreateDataPayload!

Fields on CreateSingleCargoInput

Use AddressInput for loadAddress and unloadAddress (see AddressInput).

ReqFieldTypeDescription
requiredexternalIdString!Your unique row ID
requireddateDateTime!Date of shipment (UTC)
requiredloadAddressAddressInput!Origin / pickup
requiredunloadAddressAddressInput!Destination / delivery
requireddistanceFloat!Distance in kilometres
requiredweightFloat!Mass in kilograms for this upstream transportation & distribution record
requiredcustomerIdString!Target customer ID
requiredclassificationKeyString!UNSPSC classification key
requiredtransportTypeCargoTransportTypeEnum!INBOUND, OUTBOUND, or INTERNAL
requiredassetLabelString!Asset tied to the shipment (vehicle, site, etc.)
requireddataLabelString!Distinct label for the move (B/L, booking ref., etc.)
optionalsupplierLabelStringCarrier or forwarder name
optionalreferenceStringExtra context (invoice, notes)

Mutation

mutation createCargo($input: CreateCargoInput!) {
  createCargo(input: $input) {
    success
    message
    rows
  }
}

Variables — Acme outbound trailer (same day)

{
  "input": {
    "data": [{
      "externalId": "ACME-UTD-2024-03-088",
      "date": "2024-03-08T06:00:00.000Z",
      "loadAddress": {
        "country": "IS",
        "city": "Reykjavík",
        "address": "Acme depot, Sundagarðar",
        "postalCode": "104"
      },
      "unloadAddress": {
        "country": "IS",
        "city": "Keflavík",
        "address": "Keflavík logistics terminal",
        "postalCode": "235"
      },
      "distance": 52,
      "weight": 18500,
      "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
      "classificationKey": "78121603",
      "transportType": "OUTBOUND",
      "assetLabel": "Trailer T-402",
      "dataLabel": "B/L ICE-2024-7781",
      "supplierLabel": "Northwind Line",
      "reference": "INV 99204"
    }]
  }
}

Response

{"data": {"createCargo": {"success": true, "message": "OK", "rows": 1}}}

2Submit MUTATION createWaste #

Submit waste disposal records. For waste haulers and organisations self-reporting waste data. Note: createWaste returns CreateWastePayload with a status enum, unlike most mutations which return a success boolean.

Two classification codes per row

Waste records carry two UNSPSC codes — one for what the waste is (e.g. mixed paper, food waste, hazardous chemicals) and one for how it's treated (recycled, landfilled, incinerated). Both drive the emissions calculation — the same waste type has very different emissions depending on treatment.

In createWaste these map to wasteClassificationKey (what the waste is) and treatmentClassificationKey (how it is treated) on each CreateSingleWasteInput row — the same split as in the Waste CSV template.

MUTATIONWastecreateWaste

Returns CreateWastePayload! — note: different payload type

Fields on CreateSingleWasteInput

You can submit one or many waste records in data. Quantity is reported as mass in kilograms using the kg field (cubic metres / m3 are not supported for waste). Use AddressInput for the collection / pickup site (see AddressInput).

ReqFieldTypeDescription
requiredexternalIdString!Your unique row ID
requiredtimeDateTime!Pickup time (UTC)
requiredwasteDescriptionString!What was collected or disposed
requiredcustomerIdString!Target customer ID
requiredwasteClassificationKeyString!UNSPSC key for the waste stream (from getClassifications)
requiredtreatmentClassificationKeyString!Treatment / disposal UNSPSC key
requiredkgFloat!Mass in kilograms for this row (m3 is not supported for waste)
requiredpickUpAddressAddressInput!Customer / collection site
optionalpickUpLocationStringExtra location detail (gate, bay, instructions)
optionalsupplierLabelStringWaste pickup provider name
optionalreferenceStringTicket, weighbridge, or invoice ref.

Mutation

mutation createWaste($input: CreateWasteInput!) {
  createWaste(input: $input) {
    status
    rows
  }
}

Variables — Acme's mixed waste pickup

{
  "input": {
    "data": [{
      "externalId": "ACME-WASTE-202403-001",
      "time": "2024-03-05T07:30:00.000Z",
      "wasteDescription": "Mixed municipal — Reykjavík depot",
      "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
      "wasteClassificationKey": "21-07-01",
      "treatmentClassificationKey": "77-10-20",
      "pickUpAddress": { "country": "IS", "city": "Reykjavík" },
      "kg": 1200
    }]
  }
}

Response

{"data": {"createWaste": {"status": "SUCCESS", "rows": 1}}}

2Submit MUTATION createFlightTravel #

Submit air travel records. For travel agencies, corporate travel management, and organisations self-reporting flight emissions. Captures origin/destination airports, cabin class, and number of passengers.

MUTATIONFlight travelcreateFlightTravel

Returns CreateDataPayload!

Fields on CreateSingleFlightTravelInput

Cabin or fare class is usually encoded in the UNSPSC classificationKey you choose from getClassifications.

ReqFieldTypeDescription
requiredexternalIdString!Your unique row ID
requireddateOfTravelDateTime!Flight date/time (UTC)
requiredfromIATAString!Origin airport (IATA)
requiredtoIATAString!Destination airport (IATA)
requirednumberOfPassengersInt!Passenger count
requiredtravelDescriptionString!Trip purpose or label
requiredassetDescriptionString!Asset tied to the activity (vehicle, site, etc.)
requiredcustomerIdString!Target customer ID
optionalclassificationKeyStringUNSPSC travel classification
optionalsupplierLabelStringAgency or TMC name
optionalreferenceStringPNR, invoice, or policy ref.

Mutation

mutation createFlightTravel($input: CreateFlightTravelInput!) {
  createFlightTravel(input: $input) {
    success
    message
    rows
  }
}

Variables — Acme leg KEF → CPH

{
  "input": {
    "data": [{
      "externalId": "ACME-FLT-2024-03-014",
      "dateOfTravel": "2024-03-12T14:20:00.000Z",
      "fromIATA": "KEF",
      "toIATA": "CPH",
      "numberOfPassengers": 1,
      "travelDescription": "Client meeting — Copenhagen",
      "assetDescription": "Sales team / EMEA",
      "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
      "classificationKey": "40101501",
      "reference": "PNR ABC123"
    }]
  }
}

Response

{"data": {"createFlightTravel": {"success": true, "message": "OK", "rows": 1}}}

2Submit MUTATION createSurfaceTravel #

Submit non-aerial transportation records. Covers road, rail, and sea travel. Use for company vehicle mileage, employee commute, ferry trips, or any surface-mode transport.

MUTATIONSurface travelcreateSurfaceTravel

Returns CreateDataPayload!

Fields on CreateSingleSurfaceTravelInput

Surface travel covers road, rail, and sea modes (for example company vehicles, buses, trains, or ferries). Use AddressInput for fromAddress and toAddress (see AddressInput).

ReqFieldTypeDescription
requiredexternalIdString!Your unique row ID
requireddateOfTravelDateTime!Trip start (UTC)
requiredfromAddressAddressInput!Origin
requiredtoAddressAddressInput!Destination
requireddistanceTraveledFloat!Distance in kilometres
requirednumberOfPassengersInt!Passenger count
requiredtravelDescriptionString!Mode and context (e.g. ferry, rail commute)
requiredassetDescriptionString!Asset tied to the activity (vehicle, site, etc.)
requiredcustomerIdString!Target customer ID
optionalclassificationKeyStringUNSPSC surface-travel key
optionalsupplierLabelStringOperator name
optionalreferenceStringTicket or invoice ref.

Mutation

mutation createSurfaceTravel($input: CreateSurfaceTravelInput!) {
  createSurfaceTravel(input: $input) {
    success
    message
    rows
  }
}

Variables — Acme ferry leg (road + sea)

{
  "input": {
    "data": [{
      "externalId": "ACME-SURF-2024-03-021",
      "dateOfTravel": "2024-03-15T07:00:00.000Z",
      "fromAddress": {
        "country": "IS",
        "city": "Landeyjahöfn",
        "label": "Ferry terminal"
      },
      "toAddress": {
        "country": "IS",
        "city": "Vestmannaeyjar",
        "label": "Herjólfur dock"
      },
      "distanceTraveled": 14,
      "numberOfPassengers": 2,
      "travelDescription": "Ferry — staff site visit",
      "assetDescription": "Field operations / South",
      "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
      "classificationKey": "78121601",
      "supplierLabel": "Herjólfur",
      "reference": "Ticket H-44102"
    }]
  }
}

Response

{"data": {"createSurfaceTravel": {"success": true, "message": "OK", "rows": 1}}}

2Submit MUTATION createHotelTravel #

Submit hotel stay records. For travel agencies and organisations tracking Scope 3 business travel. Each record captures number of nights, country, and optionally hotel name and city.

MUTATIONHotel stayscreateHotelTravel

Returns CreateDataPayload!

Fields on CreateSingleHotelTravelInput

address identifies the country where the hotel is located (see AddressInput).

ReqFieldTypeDescription
requiredexternalIdString!Your unique row ID
requireddateDateTime!Check-in date (UTC)
requiredaddressAddressInput!Hotel location (at least country)
requirednumberOfNightsInt!Nights booked
requirednumberOfRoomsInt!Rooms booked
optionalnumberOfGuestsIntGuests (optional if unknown)
requiredstayDescriptionString!Free-text description of the stay — for example the hotel name, a conference or event, the customer or account you visited, or an internal project label.
requiredassetDescriptionString!Asset tied to the activity (vehicle, site, etc.)
requiredcustomerIdString!Target customer ID
optionalclassificationKeyStringUNSPSC hotel stay key
optionalsupplierLabelStringChain or booking channel
optionalreferenceStringConfirmation or invoice ref.

Mutation

mutation createHotelTravel($input: CreateHotelTravelInput!) {
  createHotelTravel(input: $input) {
    success
    message
    rows
  }
}

Variables — Acme two-night stay in Copenhagen

{
  "input": {
    "data": [{
      "externalId": "ACME-HOTEL-2024-03-009",
      "date": "2024-03-18T15:00:00.000Z",
      "address": {
        "country": "DK",
        "city": "Copenhagen",
        "address": "Tivoli Hotel & Congress Center",
        "postalCode": "1630"
      },
      "numberOfNights": 2,
      "numberOfRooms": 1,
      "numberOfGuests": 1,
      "stayDescription": "Tivoli Hotel — client workshop (EMEA sales)",
      "assetDescription": "Sales team / EMEA",
      "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
      "classificationKey": "90111501",
      "supplierLabel": "CorporateTravel.dk",
      "reference": "Conf. DK-88331"
    }]
  }
}

Response

{"data": {"createHotelTravel": {"success": true, "message": "OK", "rows": 1}}}

2Submit MUTATION createGoodsServices #

Submit purchased goods and services data. Covers Scope 3 Category 1 spend-based reporting. Each record represents monetary spend on a product or service — Klappir converts this to an emissions estimate using spend-based emission factors.

MUTATIONGoods & servicescreateGoodsServices

Returns CreateDataPayload!

Fields on CreateSingleGoodsServicesInput

Other members on CreateSingleGoodsServicesInput may exist beyond this table — confirm names and nullability with GraphQL introspection for your environment.

Each record must include at least one of kg (kilograms purchased) and kgco2e (supplier-verified CO₂e for the line item); you may send both. When both are present, Klappir uses kgco2e as the authoritative emissions value and keeps kg for traceability — the same rule as the Goods & Services CSV template.

ReqFieldTypeDescription
requiredexternalIdString!Your unique row ID
requireddateDateTime!Purchase / invoice date (UTC)
requiredproductLabelString!Product or line item as on the invoice
requiredassetDescriptionString!Asset tied to the activity (vehicle, site, etc.)
requiredclassificationKeyString!UNSPSC key from getClassifications
requiredcustomerIdString!Target customer ID
optionalkgFloatMass purchased in kilograms (quantity path). See the note above for row-level requirements.
optionalkgco2eFloatDirect CO₂e for the line item from a supplier-verified figure (emission path). When both kg and kgco2e are sent, kgco2e is authoritative for emissions.
optionalsupplierLabelStringVendor display name
optionalreferenceStringPO, contract, or invoice ref.

Mutation

mutation createGoodsServices($input: CreateGoodsServicesInput!) {
  createGoodsServices(input: $input) {
    success
    message
    rows
  }
}

Variables — Acme quantity + supplier CO₂e (both)

{
  "input": {
    "data": [{
      "externalId": "ACME-GS-2024-03-044",
      "date": "2024-03-22T00:00:00.000Z",
      "productLabel": "Office paper — A4, recycled 80gsm",
      "assetDescription": "HQ — Borgartún",
      "classificationKey": "14111507",
      "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
      "kg": 120,
      "kgco2e": 86.4,
      "supplierLabel": "Nordic Office Supply",
      "reference": "PO 77812"
    }]
  }
}

You can omit kgco2e and send only kg (or only kgco2e if you have a verified emissions figure without mass).

Response

{"data": {"createGoodsServices": {"success": true, "message": "OK", "rows": 1}}}

Edit, update & delete data #

The same lifecycle rules apply to the API as to SFTP. Every create* mutation is an upsert keyed by externalId (SFTP Unique ID). Send the mutation again to change a row; send quantity fields as zero to remove it. There is no separate delete mutation.

SFTP equivalent

CSV re-upload with the same Unique ID updates a row; Value = 0 deletes it. Full SFTP walkthrough: Edit / update / delete. Field mapping: SFTP and API field alignment.

Update an existing record

To correct data that already reached Klappir:

  1. Call the same create* mutation you used originally (createFuel, createWaste, and so on).
  2. Include a record object with the same externalId as before.
  3. Change any fields that need updating (time, classificationKey, quantities, addresses, labels, etc.).

Klappir updates the existing row instead of creating a duplicate. The mutation response can still show success: true — confirm the change in the Klappir platform if needed.

Delete a record

To remove a record, call the same mutation again with the same externalId and set the quantity field(s) you originally used to zero:

MutationQuantity field(s) to set to 0
createFuelThe field(s) you populated — liters, kg, m3, and/or kwh
createCargoweight and/or distanceTraveled (whichever you sent)
createWastekg
createFlightTravelpassengers (and/or distance fields if you used them)
createSurfaceTraveldistanceTraveled and/or passengers
createHotelTravelnights
createGoodsServiceskg and/or kgco2e

Omitting a record from a new batch does not delete it. Only an explicit zero-quantity resubmit with the same externalId removes the row — the same rule as on SFTP.

Example — update and delete fuel

Acme first submitted diesel for March. They fix the date, then remove a bad row:

// Update — same externalId, corrected time
{
  "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
  "externalId": "ACME-FUEL-2024-03-002",
  "classificationKey": "15-10-15-05-K001",
  "time": "2024-03-02T12:00:00Z",
  "liters": 312.0
}

// Delete — same externalId, zero quantity
{
  "customerId": "afaf2111-f5cc-4c69-b806-46d318e28b5d",
  "externalId": "ACME-FUEL-2024-03-099",
  "classificationKey": "15-10-15-05-K001",
  "time": "2024-03-15T12:00:00Z",
  "liters": 0
}

Recommended correction workflow

StepAction
1Confirm the record exists in the Klappir platform (wrong totals often mean a failed first submit).
2Build a minimal mutation payload — one corrected or zero-quantity record is enough.
3POST the mutation; log success, message, and rows from the response.
4Verify in the platform. If it still looks wrong, see Troubleshooting common API errors.
A
Acme fixes a duplicate

Acme accidentally submitted ACME-FUEL-2024-03-014 twice with different dates. They keep the correct version unchanged and send a second mutation with the stray externalId and liters: 0 to delete the bad copy.

What's next, after your first integration #

Acme's first end-to-end run worked. Now they need to make this a reliable production pipeline — and so will you. A few directions to think about:

Schedule it

Most integrations run on a cron schedule — nightly for the previous day's data. Acme uses a single cron job that submits each day.

Handle errors gracefully

Network errors, validation failures, and transient 5xxs all happen. Retry with backoff. When success: false comes back, log the message field — it usually tells you which record failed and why.

Batch sensibly

Don't submit one record at a time. Most integrations batch by day or by customer. If a batch is too large, split on a natural boundary like customerId.

Pick a stable externalId scheme

Once you've chosen a format, don't change it. Acme uses ACME-FUEL-2024-03-001 — your future self will thank you when reconciling or correcting historical records. To fix or remove rows later, see Edit / update / delete.

Refresh the cache periodically

getClassifications can change as the taxonomy grows. Cache aggressively but refresh on a schedule so new codes show up.

getActiveCustomers and getClassifications can change. Cache aggressively but refresh weekly so new customers and codes show up.

Monitor in the dashboard

Verify data appears in Klappir after submission. Set up alerts in your monitoring stack tied to the rows response — a sudden drop usually means upstream data is missing.

When something goes wrong

For common HTTP and GraphQL payload issues (401, validation errors, customerId, classificationKey), see the Troubleshooting common API errors table in Getting started. If you still need help, email service@klappir.com with your x-api-key identifier (not the key itself), the timestamp of the request, and the response body.

Type reference #

Key types referenced throughout the API. Standard GraphQL scalars (String, Boolean, Int, Float, ID) follow standard definitions. Tables below use the same Req column as mutation inputs: required vs optional.

Customer

ReqFieldTypeDescription
requiredidID!UUID — use as customerId in all mutations. Direct customers: same value as Settings → System → Entity IDs in the Klappir Platform.
requirednameString!Company display name
requiredregistrationString!Company registration number
optionaldescriptionStringOptional description
optionaladdressCustomerAddressPhysical address fields

CreateDataPayload

Returned by most create* mutations.

ReqFieldTypeDescription
requiredsuccessBoolean!Whether the operation succeeded
optionalmessageStringHuman-readable status or error message
optionalrowsIntNumber of records accepted

Classification

One row from getClassifications.

ReqFieldTypeDescription
requiredkeyID!Use as classificationKey / waste keys on mutations
requiredlevelInt!Hierarchy depth
requireddataTypeDataTypeEnum!Target data category
requiredusageString!Internal usage label
requireddescriptionString!Display label

GetClassificationsInput

ReqFieldTypeDescription
optionalkeyPrefixStringReturn keys starting with this prefix
optionaldataType[DataTypeEnum!]Filter to one or more data types — e.g. ["FUEL", "WASTE"]

CreateWastePayload

Returned specifically by createWaste.

ReqFieldTypeDescription
requiredstatusStatusEnum!SUCCESS or FAILURE
requiredrowsInt!Number of records accepted

DataTypeEnum

ValueDescription
FUELLiquid and gaseous fuels — createFuel, Fuel template
WASTEWaste disposal — createWaste, Waste template
FLIGHTAir travel — createFlightTravel, Flight template
SURFACE_TRAVELRoad, rail, or sea travel — createSurfaceTravel, Surface template
HOTELHotel stays — createHotelTravel, Hotel template
CARGOUpstream transportation & distribution — createCargo, Upstream template
GOODS_SERVICESPurchased goods and services — createGoodsServices, Goods & Services template

CargoTransportTypeEnum (upstream transportation & distribution)

ValueDescription
INBOUNDFreight arriving at customer's location
OUTBOUNDFreight leaving customer's location
INTERNALInternal logistics between sites

AddressInput

Nested object for structured locations on fuel, upstream transportation & distribution, hotel, surface travel, and waste collection (pickup) payloads. country is an ISO 3166-1 alpha-2 code; other members are optional text or coordinates.

ReqFieldTypeDescription
requiredcountryCountryIsoLocode!ISO country code (e.g. IS)
optionaladdressStringStreet line
optionalcityStringCity
optionalpostalCodeStringPostal or ZIP code
optionalmunicipalityStringMunicipality or district
optionallabelStringShort site label (yard, pier, building)

DateTime

ISO 8601 datetime string. Example: "2024-03-01T08:00:00.000Z". Always use UTC (suffix Z).

api.klappir.com/graphql