API documentation v1.4.0
REST interface for trips, vehicles, error codes and measurements of the iOS app OBDRest · OpenAPI 3.1.0 · download openapi.yaml
Step 1🔑 Log in with curl
Every call except /health, /auth/login and /auth/register requires authentication. First get a token and then use it as the Authorization: Bearer header. For the API you need your own account (register for free). For security reasons, the demo account demo can only be used in the browser – the server responds to API calls with 403.
Set credentials (own account – the demo account has no API access)
export BENUTZER='max.mustermann'
export PASSWORT='geheim-und-lang'
Log in and store token in variable
TOKEN=$(curl -s https://app.fahrten.fusenigk.de/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d "{\"username\":\"$BENUTZER\",\"password\":\"$PASSWORT\",\"client\":\"app\"}" | jq -r .daten.token)
echo "$TOKEN"
Then every call with Bearer token
curl -s https://app.fahrten.fusenigk.de/api/v1/auth/me -H "Authorization: Bearer $TOKEN"
Alternative without token – HTTP Basic
curl -s -u "$BENUTZER:$PASSWORT" https://app.fahrten.fusenigk.de/api/v1/auth/me
Response
{
"ok": true,
"daten": {
"benutzer": {
"id": 7,
"username": "max.mustermann",
"name": "Max Mustermann",
"email": "max@example.org",
"rolle": "user",
"istAdmin": false,
"darfSenden": true,
"darfLoeschen": true,
"darfApi": true,
"aktiv": true,
"anmeldung": "local"
},
"methode": "local",
"token": "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiRGVtby1CZW51dHplciIsImFydCI6ImFwcCJ9.…",
"ablauf": "2027-02-05T09:20:00.000Z"
}
}
Without jq: copy the token from the data.token field of the response. Failed attempts: 401 (wrong), 403 (not unlocked), 429 (too many attempts).
📘 Basics
The iOS app OBDRest reads vehicle data via OBD2 adapter (ELM327, Bluetooth LE), records trips and sends them to this server. The same interface can be used to read, manage and export all data – or to implement your own server to which the app sends its data (server dialog in the app: address, user, password).
Response format: All JSON responses have the envelope { "ok": true, "data": … } or { "ok": false, "error": "plain text", "details": … }. Times are ISO 8601 in UTC.
Authentication (read first):
POST /auth/login with username and password (SSO account or local account) → data.token.- Every further call with header
Authorization: Bearer <token>. Tokens for client: "app" are valid for 90 days, for client: "web" for 12 hours (then additionally as httpOnly cookie fahrten_session). - Alternatively for scripts: HTTP Basic (
curl -u user:password).
Permissions: Normal users only see and change their own data. Administrators see the data of all users with ?all=1 or that of one user with ?user=<id> and manage users under /admin/users.
Uploads are idempotent: Trips, fault readings and snapshots carry a UUID from the app; vehicles are identified via user + vehicle identification number (VIN). Resending does not create duplicates.
Server
https://app.fahrten.fusenigk.de/api/v1 – Production (hosted)
https://fahrten.fusenigk.de/api/v1 – Redirect for older app installations
http://localhost:3330/api/v1 – On-premises / own installation (Docker Compose)
Every JSON response is an envelope: {"ok": true, "daten": …} on success, {"ok": false, "fehler": "Plain text", "details": …} on errors. Exports (CSV, JSON file, GPX) return the file directly.
| Status | Meaning |
|---|
| 200/201 | Success |
| 400 | Invalid input (zod validation, details names the field) |
| 401 | Not signed in or token expired → sign in again |
| 403 | No permission (e.g. send, delete, administration) |
| 404 | Not found – also if the record belongs to another user |
| 409/429 | Conflict (name taken) / too many requests |
| 500/503 | Server error / SSO or database unreachable |
Authentication
Login, registration, logout, own user
POST/auth/login🌐 public
Sign in and obtain token
Checks the password via SSO (Keycloak, realm fusenigk) or – for local accounts – against the stored scrypt hash. Only users who are enabled in the app (table users) receive a token. The call writes nothing to the database. After 10 failed attempts per IP in 15 minutes, the server responds with 429.
API right: Accounts without the darfApi right (e.g. the demo account demo) receive the response 403 with client: "app" (grund: api_disabled) – only after the password has been verified. Such accounts can only sign in in the browser; bearer tokens and HTTP Basic are also rejected for them with 403.
Request (application/json) – schema LoginAnfrage
{
"username": "max.mustermann",
"password": "geheim-und-lang",
"client": "app"
}
Responses
| 200 | Signed in |
| 400 | Invalid input |
| 401 | Username or password incorrect (also for unknown user – no enumeration) |
| 403 | Password correct, but account not enabled, locked or without API right (grund: api_disabled, e.g. demo account) |
| 429 | Too many failed attempts |
| 503 | SSO server unreachable |
Example response
{
"ok": true,
"daten": {
"benutzer": {
"id": 7,
"username": "max.mustermann",
"name": "Max Mustermann",
"email": "max@example.org",
"rolle": "user",
"istAdmin": false,
"darfSenden": true,
"darfLoeschen": true,
"darfApi": true,
"aktiv": true,
"anmeldung": "local"
},
"methode": "local",
"token": "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiRGVtby1CZW51dHplciIsImFydCI6ImFwcCJ9.…",
"ablauf": "2027-02-05T09:20:00.000Z"
}
}
curl – set credentials (own account – the demo account has no API access)
export BENUTZER='max.mustermann'
export PASSWORT='geheim-und-lang'
curl – log in and store token in variable
TOKEN=$(curl -s https://app.fahrten.fusenigk.de/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d "{\"username\":\"$BENUTZER\",\"password\":\"$PASSWORT\",\"client\":\"app\"}" | jq -r .daten.token)
echo "$TOKEN"
curl – then every call with bearer token
curl -s https://app.fahrten.fusenigk.de/api/v1/auth/me -H "Authorization: Bearer $TOKEN"
curl – alternative without token – HTTP Basic
curl -s -u "$BENUTZER:$PASSWORT" https://app.fahrten.fusenigk.de/api/v1/auth/me
POST/auth/registrieren🌐 public
Create local account
Creates a local user (role user, may send and delete own trips) in the database and signs them in directly. Reserved names (admin, demo, …) and names already taken → 409. At most 5 registrations per IP in 15 minutes.
Request (application/json) – schema Registrierung
{
"username": "max.mustermann",
"name": "Max Mustermann",
"email": "max@example.org",
"passwort": "geheim-und-lang",
"client": "app"
}
Responses
| 201 | Created and signed in (response like login) |
| 400 | Invalid input |
| 409 | Name reserved or taken |
| 429 | Too many registrations |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/auth/registrieren \
-H 'Content-Type: application/json' \
-d '{"username":"max.mustermann","name":"Max Mustermann","email":"max@example.org","passwort":"geheim-und-lang","client":"app"}'
GET/auth/me🔒 Sign-in required
Signed-in user with rights
Responses
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/auth/me -H "Authorization: Bearer $TOKEN"
POST/auth/logout🌐 public
Delete session cookie
Bearer tokens are stateless (JWT) and expire; logging out only affects the browser cookie.
Responses
| 200 | Signed out |
| 429 | Too many requests |
curl
curl -s -X POST https://app.fahrten.fusenigk.de/api/v1/auth/logout
Synchronization
Uploads from the iOS app (also authoritative for custom server implementations)
POST/sync/fahrten🔒 Sign-in required
Send trips including measurement points
Accepts 1–50 trips in the app's Codable format (Trip). Existing trips with the same UUID are replaced (including all measurement points); a UUID of a different user is never overwritten (status: fremd). The vehicle is assigned or created via vin. Right darfSenden required.
Request (application/json)
Responses
| 200 | Result per trip |
| 400 | Invalid input |
| 401 | Not signed in |
| 403 | No permission to send |
Example response
{
"ok": true,
"daten": {
"fahrten": [
{
"id": "6f1b2c3d-4e5f-4a1b-9c2d-0123456789ab",
"status": "neu",
"messpunkte": 1834
}
]
}
}
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/fahrten \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"fahrzeugName":"GLC 220 d","trips":[{"id":"6F1B2C3D-4E5F-4A1B-9C2D-0123456789AB",
"startDate":"2026-11-07T07:00:00Z","endDate":"2026-11-07T07:24:00Z","kind":"geschaeftlich",
"note":"Kundentermin","fuelType":"diesel","vin":"WDC2539051F123456","isDemo":false,
"summary":{"distanceKm":18.4,"litersPer100km":6.1,"eco":{"score":81,"factors":[]}},
"samples":[{"time":"2026-11-07T07:00:02Z","speed":12.5,"rpm":1100,"latitude":52.52,"longitude":13.405}]}]}'
POST/sync/fahrzeug🔒 Sign-in required
Send vehicle and control unit data (OBD mode 09)
Request (application/json) – schema FahrzeugUpload
Responses
| 200 | Vehicle created/updated |
| 403 | No permission to send |
Example response
{
"ok": true,
"daten": {
"fahrzeugId": 4
}
}
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/fahrzeug \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"vin":"WDC2539051F123456","name":"Mercedes-Benz GLC (2015)","fuelType":"diesel",
"protocol":"ISO 15765-4 CAN (11 Bit, 500 kBaud)","adapter":"IOS-Vlink",
"ecus":[{"ecu":"7E8","name":"ECM – EngineControl","calibrationIDs":["6519015400"],"calibrationVerificationNumbers":["1A2B3C4D"]}]}'
POST/sync/fehler🔒 Sign-in required
Send fault memory reading and fault history
Stores the reading raw and consolidates the history per code (first/last seen, count, active).
Request (application/json) – schema FehlerUpload
Responses
Example response
{
"ok": true,
"daten": {
"id": "0d6e…",
"neu": true,
"codes": 2,
"aktiv": 1
}
}
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/fehler \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"id":"0D6E2A11-1C2B-4E7F-9A10-2B3C4D5E6F70","vin":"WDC2539051F123456","readAt":"2026-11-07T07:01:00Z",
"entries":[{"code":"P2002","kind":"stored","ecu":"7E8","description":"Dieselpartikelfilter – Wirkungsgrad unter Schwellenwert","udsStatus":47}],
"udsECUs":["7E8"],"history":[]}'
POST/sync/messwerte🔒 Sign-in required
Send snapshot of all live measurement values
Request (application/json) – schema Momentaufnahme
Responses
Example response
{
"ok": true,
"daten": {
"id": "9a…",
"neu": true,
"werte": 42
}
}
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/messwerte \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"id":"9A1B2C3D-0000-4000-8000-000000000001","vin":"WDC2539051F123456","takenAt":"2026-11-07T07:10:00Z",
"voltage":14.1,"protocol":"ISO 15765-4 CAN","readings":[{"pid":"0C","ecu":"7E8","name":"Drehzahl","value":1850,"unit":"U/min","group":"Motor"}],
"findings":[{"title":"Ladespannung in Ordnung","detail":"14,1 V","severity":"ok"}]}'
GET/sync/index🔒 Sign-in required
Own trip IDs with modification timestamp
Basis for synchronization in the app – missing trips are loaded, assignments changed on the web are adopted.
Responses
Example response
{
"ok": true,
"daten": {
"fahrten": [
{
"id": "6F1B2C3D-4E5F-4A1B-9C2D-0123456789AB",
"aktualisiert": "2026-11-07T08:00:00.000Z",
"messpunkte": 1834
}
]
}
}
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/index -H "Authorization: Bearer $TOKEN" | jq '.daten.fahrten | length'
POST/sync/ausgaben🔒 Sign-in required
Send expenses from the app (1–500, idempotent via the UUID)
Request (application/json)
{
"ausgaben": [
{
"kategorie": "tanken",
"datum": "2026-11-07T08:15:00Z",
"betrag": 82.45,
"fahrzeugId": 4,
"anbieter": "Aral Unter den Linden",
"liter": 48.6,
"preisJeLiter": 1.699,
"kraftstoff": "Diesel",
"vollgetankt": true,
"kmStand": 129980,
"id": "3c1e5a2b-7d4f-4e8a-9b1c-2d3e4f5a6b7c"
}
]
}
Responses
| 200 | Status per expense (new/updated/fremd) |
| 401 | Not signed in |
| 403 | no permission |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/ausgaben -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d @ausgaben.json
POST/sync/kilometerstand🔒 Sign-in required
Send odometer readings from the app (1–500, idempotent via the UUID, vehicle via the VIN)
Request (application/json)
{
"kilometerstaende": [
{
"id": "5b0e7d1c-2a4f-4c3e-8d9a-1f2e3d4c5b6a",
"vin": "WDC2539091F000000",
"km": 128450,
"datum": "2026-11-07T09:00:00Z"
}
]
}
Responses
| 200 | Status per entry (new/updated/fremd/kein-fahrzeug) |
| 401 | Not signed in |
| 403 | no permission |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/kilometerstand -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d @kilometerstaende.json
POST/sync/unknown🔒 Login required
Report unknown codes from the app (PIDs without decoder, error codes without description)
Aggregated per user + vehicle (VIN) + type + code + control unit; the last 20 raw data records are retained as samples. Administrators evaluate the entries under /admin/unknown.
Request (application/json)
{
"vin": "WDC2539091F000000",
"protokoll": "ISO 15765-4 CAN (11 bit, 500 kbaud)",
"adapter": "Vgate iCar Pro",
"codes": [
{
"art": "pid",
"code": "8B",
"ecu": "7E8",
"modus": "01",
"roh": "1F 00 2A 10",
"gesehen": "2026-11-07T18:00:00Z"
}
]
}
Responses
| 200 | Vehicle ID, number of codes, of which new |
| 400 | invalid input (e.g. raw data not as hex) |
| 401 | Not signed in |
| 403 | no permission to send |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/unbekannt -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d @unbekannt.json
GET/sync/ausgaben-index🔒 Sign-in required
Own expense IDs with modification timestamp
Responses
| 200 | `daten.ausgaben[]` |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/sync/ausgaben-index -H "Authorization: Bearer $TOKEN"
Trips
Read trips, assign logbook category (Private/Business/Commute), delete
GET/fahrten🔒 Sign-in required
List trips
Parameters
| Name | Location | Type | Description |
|---|
alle | query | string (1) | Administrators only: data of all users |
benutzer | query | integer | Administrators only: data of a specific user |
fahrzeug | query | integer | Vehicle ID |
von | query | string · date | Example: 2026-10-01 |
bis | query | string · date | Example: 2026-10-31 |
art | query | FahrtArt | |
suche | query | string | Full text in addresses and note |
limit | query | integer | |
offset | query | integer | |
Responses
| 200 | Page of the trip list |
| 401 | Not signed in |
curl – business trips in October
curl -s 'https://app.fahrten.fusenigk.de/api/v1/fahrten?art=geschaeftlich&von=2026-10-01&bis=2026-10-31' \
-H "Authorization: Bearer $TOKEN" | jq '.daten.fahrten[] | {start, km, startAdresse, zielAdresse, notiz}'
PATCH/fahrten🔒 Sign-in required
Bulk assignment Private / Business / Commute
Request (application/json)
Responses
| 200 | Number of changed trips |
| 401 | Not signed in |
Example response
{
"ok": true,
"daten": {
"geaendert": 2
}
}
curl
curl -s -X PATCH https://app.fahrten.fusenigk.de/api/v1/fahrten \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"ids":["6f1b2c3d-4e5f-4a1b-9c2d-0123456789ab","7a2c…"],"kind":"geschaeftlich"}'
GET/fahrten/{id}🔒 Sign-in required
Trip with all measurement points
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
format | query | string (app) | app returns the iOS app's Codable format (Trip)
|
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | Trip incl. `punkte` (or `samples` with format=app) |
| 404 | Not found (or not visible) |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fahrten/$ID -H "Authorization: Bearer $TOKEN" | jq '.daten | {km, art, punkte: (.punkte|length)}'
PATCH/fahrten/{id}🔒 Sign-in required
Change type, label and description of a trip
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Request (application/json)
{
"kind": "geschaeftlich",
"note": "Kundentermin Adlershof",
"details": "Projektbesprechung mit Frau Muster.\nRückweg über Lager Schönefeld (Material abgeholt)."
}
Responses
| 200 | changed |
| 404 | Not found (or not visible) |
curl
curl -s -X PATCH https://app.fahrten.fusenigk.de/api/v1/fahrten/$ID \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"kind":"geschaeftlich","note":"Kundentermin Adlershof","details":"Projektbesprechung.\nRückweg über Lager Schönefeld."}'
DELETE/fahrten/{id}🔒 Sign-in required
Delete trip (right darfLoeschen)
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | deleted |
| 403 | No permission |
| 404 | Not found (or not visible) |
curl
curl -s -X DELETE https://app.fahrten.fusenigk.de/api/v1/fahrten/$ID -H "Authorization: Bearer $TOKEN"
Vehicles
Vehicles per user (identified via the vehicle identification number)
GET/fahrzeuge🔒 Sign-in required
Vehicles with key figures
Parameters
| Name | Location | Type | Description |
|---|
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | List (`daten.fahrzeuge`) |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fahrzeuge -H "Authorization: Bearer $TOKEN" | jq '.daten.fahrzeuge[] | {name, vin, fahrten, km, fehlerAktiv}'
GET/fahrzeuge/{id}🔒 Sign-in required
Vehicle with monthly history, faults, recent trips and latest measurement values
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Responses
| 200 | Vehicle |
| 404 | Not found (or not visible) |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fahrzeuge/4 -H "Authorization: Bearer $TOKEN"
GET/fahrzeuge/{id}/kilometerstand🔒 Sign-in required
Odometer – last and estimated reading, history (manual and from expenses)
current is the most recent known reading – manually recorded or as the kmStand of an entry (fueling, parking, repair, other). estimated = km + sum of trips recorded thereafter (seitherKm).
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Responses
| 200 | Odometer |
| 401 | Not signed in |
| 404 | Not found (or not visible) |
Example response
{
"ok": true,
"daten": {
"aktuell": {
"km": 128450,
"datum": "2026-11-01T09:00:00Z",
"geschaetzt": 128712,
"seitherKm": 262
},
"verlauf": [
{
"id": "3C1E5A2B-7D4F-4E8A-9B1C-2D3E4F5A6B7C",
"km": 128450,
"datum": "2026-11-01T09:00:00Z",
"quelle": "manuell",
"notiz": "Tacho abgelesen",
"ausgabeId": null
}
]
}
}
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fahrzeuge/4/kilometerstand -H "Authorization: Bearer $TOKEN"
POST/fahrzeuge/{id}/kilometerstand🔒 Login required
Record mileage (idempotent via optional UUID)
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Request (application/json) – Schema Kilometerstand
{
"km": 128450,
"datum": "2026-11-07T09:00:00Z",
"notiz": "Tacho abgelesen"
}
Responses
| 200 | updated |
| 201 | newly created |
| 400 | invalid input |
| 401 | Not signed in |
| 403 | no permission (e.g. demo access) |
| 404 | Not found (or not visible) |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fahrzeuge/4/kilometerstand \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"km": 128450, "notiz": "Tacho abgelesen"}'
DELETE/kilometerstand/{id}🔒 Login required
Delete manually recorded mileage (change readings from entries via the entry)
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | deleted |
| 401 | Not signed in |
| 403 | no permission |
| 404 | Not found (or not visible) |
curl
curl -s -X DELETE https://app.fahrten.fusenigk.de/api/v1/kilometerstand/3c1e5a2b-7d4f-4e8a-9b1c-2d3e4f5a6b7c -H "Authorization: Bearer $TOKEN"
Errors
Fault codes (DTC) and readout operations
GET/fehler🔒 Login required
Fault codes (active and old)
Parameters
| Name | Location | Type | Description |
|---|
aktiv | query | string (1) | only active codes |
fahrzeug | query | integer | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | List (`daten.fehler`) |
| 401 | Not signed in |
curl
curl -s 'https://app.fahrten.fusenigk.de/api/v1/fehler?aktiv=1' -H "Authorization: Bearer $TOKEN" | jq '.daten.fehler[] | {code, beschreibung, fahrzeug}'
GET/fehler/{id}🔒 Login required
Fault code with all readouts in which it occurred
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Responses
| 200 | Errors |
| 404 | Not found (or not visible) |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fehler/12 -H "Authorization: Bearer $TOKEN"
GET/fehler/auslesungen🔒 Login required
Readout operations of the fault memory
Responses
| 200 | List (`daten.auslesungen`) |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fehler/auslesungen -H "Authorization: Bearer $TOKEN"
Expenses
Fueling, parking, repair/maintenance, other – record, edit, evaluate
GET/ausgaben🔒 Login required
List expenses (with totals per category)
Parameters
| Name | Location | Type | Description |
|---|
category | query | string (tanken, parken, reparatur, sonstiges) | |
von | query | string · date | |
bis | query | string · date | |
fahrzeug | query | integer | |
limit | query | integer | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | `daten.ausgaben[]` (incl. number of receipts), `daten.summen`, `daten.gesamt` |
| 401 | Not signed in |
curl
curl -s 'https://app.fahrten.fusenigk.de/api/v1/ausgaben?kategorie=tanken&von=2026-01-01' -H "Authorization: Bearer $TOKEN" | jq '.daten | {gesamt, summen}'
POST/ausgaben🔒 Login required
Record expense (permission "senden")
Request (application/json) – Schema Ausgabe
{
"kategorie": "tanken",
"datum": "2026-11-07T08:15:00Z",
"betrag": 82.45,
"fahrzeugId": 4,
"anbieter": "Aral Unter den Linden",
"liter": 48.6,
"preisJeLiter": 1.699,
"kraftstoff": "Diesel",
"vollgetankt": true,
"kmStand": 129980
}
Responses
| 201 | created (detail with `belegListe`) |
| 400 | Invalid input |
| 401 | Not signed in |
| 403 | no permission |
curl – Record repair
curl -s https://app.fahrten.fusenigk.de/api/v1/ausgaben -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"kategorie":"reparatur","datum":"2026-11-05T09:00:00Z","betrag":489.60,"art":"Inspektion","anbieter":"Mercedes-Benz Niederlassung Berlin","kmStand":129310,"rechnungsNr":"R-2026-48311"}'
curl – Record parking
curl -s https://app.fahrten.fusenigk.de/api/v1/ausgaben -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"kategorie":"parken","datum":"2026-11-06T09:30:00Z","betrag":6.50,"dauerMin":120,"anbieter":"Parkhaus WISTA Adlershof"}'
GET/ausgaben/{id}🔒 Login required
Expense with receipt list
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | Expense |
| 401 | Not signed in |
| 404 | Not found (or not visible) |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/ausgaben/$AUSGABE -H "Authorization: Bearer $TOKEN"
PUT/ausgaben/{id}🔒 Login required
Edit expense
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Request (application/json) – Schema Ausgabe
{
"kategorie": "tanken",
"datum": "2026-11-07T08:15:00Z",
"betrag": 82.45,
"fahrzeugId": 4,
"anbieter": "Aral Unter den Linden",
"liter": 48.6,
"preisJeLiter": 1.699,
"kraftstoff": "Diesel",
"vollgetankt": true,
"kmStand": 129980
}
Responses
| 200 | changed |
| 401 | Not signed in |
| 404 | Not found (or not visible) |
curl
curl -s -X PUT https://app.fahrten.fusenigk.de/api/v1/ausgaben/$AUSGABE -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"kategorie":"tanken","datum":"2026-11-07T08:15:00Z","betrag":82.45,"liter":48.6,"vollgetankt":true}'
DELETE/ausgaben/{id}🔒 Login required
Delete expense including receipts (also files)
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | deleted |
| 401 | Not signed in |
| 404 | Not found (or not visible) |
curl
curl -s -X DELETE https://app.fahrten.fusenigk.de/api/v1/ausgaben/$AUSGABE -H "Authorization: Bearer $TOKEN"
GET/ausgaben/statistik🔒 Login required
Expenses per month and category, fuel price, consumption from fuel receipts, cost per km, parking locations, repair types
Parameters
| Name | Location | Type | Description |
|---|
von | query | string · date | |
bis | query | string · date | |
fahrzeug | query | integer | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | Statistics |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/ausgaben/statistik -H "Authorization: Bearer $TOKEN" | jq '.daten | {gesamt, kostenJeKm, tanken: .tanken.schnittPreis}'
Receipts & documents
Photos and PDFs for expenses (receipts) and vehicles (vehicle registration, photos, insurance) – own files only
GET/ausgaben/{id}/belege🔒 Login required
Receipts of an expense
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | `daten.belege[]` (file) |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/ausgaben/$AUSGABE/belege -H "Authorization: Bearer $TOKEN" | jq '.daten.belege[] | {dateiname, mime, url}'
POST/ausgaben/{id}/belege🔒 Login required
Upload photos/PDFs for an expense
The file type is detected by content (JPEG, PNG, WebP, HEIC, PDF); SVG/HTML are rejected (415). Identical files (SHA-256) are not stored twice. Storage on the server under uploads/<gruppe>/<untergruppe>/u<benutzer>/<uuid>.
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Request (application/json)
Responses
| 201 | `daten.belege[]` stored, `daten.fehler[]` rejected files |
| 400 | no valid file |
| 401 | Not signed in |
| 403 | no permission |
| 404 | Not found (or not visible) |
curl – Upload invoice and photo
curl -s https://app.fahrten.fusenigk.de/api/v1/ausgaben/$AUSGABE/belege -H "Authorization: Bearer $TOKEN" \
-F datei=@rechnung.pdf -F datei=@foto-quittung.jpg
GET/belege/{id}🔒 Login required
Serve file (own files only)
Serves the file inline (?download=1 as attachment). Foreign IDs result in 404 – even with a manually modified URL.
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
download | query | string (1) | |
Responses
| 200 | File |
| 401 | Not signed in |
| 404 | Not found (or not visible) |
curl
curl -s -OJ 'https://app.fahrten.fusenigk.de/api/v1/belege/$BELEG?download=1' -H "Authorization: Bearer $TOKEN"
DELETE/belege/{id}🔒 Login required
Delete file
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | deleted |
| 401 | Not signed in |
| 404 | Not found (or not visible) |
curl
curl -s -X DELETE https://app.fahrten.fusenigk.de/api/v1/belege/$BELEG -H "Authorization: Bearer $TOKEN"
GET/fahrzeuge/{id}/dateien🔒 Login required
Vehicle registration, photos and documents of a vehicle
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Responses
| 200 | `daten.dateien[]` |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/fahrzeuge/4/dateien -H "Authorization: Bearer $TOKEN"
POST/fahrzeuge/{id}/dateien🔒 Login required
Upload vehicle registration/photo/document
The file type is detected by content (JPEG, PNG, WebP, HEIC, PDF); SVG/HTML are rejected (415). Identical files (SHA-256) are not stored twice. Storage on the server under uploads/<gruppe>/<untergruppe>/u<benutzer>/<uuid>.
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Request (application/json)
Responses
| 201 | `daten.dateien[]`, `daten.fehler[]` |
| 401 | Not signed in |
| 403 | no permission |
| 404 | Not found (or not visible) |
curl – Upload vehicle registration
curl -s https://app.fahrten.fusenigk.de/api/v1/fahrzeuge/4/dateien -H "Authorization: Bearer $TOKEN" \
-F dokumentart=fahrzeugschein -F datei=@fahrzeugschein.pdf
Measurements
Snapshots of all live data
GET/messwerte🔒 Login required
Snapshots of live data
Responses
| 200 | List (`daten.momentaufnahmen`) |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/messwerte -H "Authorization: Bearer $TOKEN"
GET/messwerte/{id}🔒 Login required
All values and findings of a snapshot
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | Snapshot |
| 404 | Not found (or not visible) |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/messwerte/$SNAP -H "Authorization: Bearer $TOKEN" | jq '.daten.werte[] | "\(.name): \(.value) \(.unit)"'
GET/messwerte/verlauf🔒 Login required
History of a measured value across all snapshots
Parameters
| Name | Location | Type | Description |
|---|
pid | query | string | OBD PID hexadecimal, e.g. 42 = control unit voltage, 05 = coolant |
Responses
| 200 | History |
| 401 | Not signed in |
Example response
{
"ok": true,
"daten": {
"pid": "42",
"verlauf": [
{
"zeit": "2026-10-01T08:12:00.000Z",
"wert": 14.12,
"einheit": "V"
}
]
}
}
curl
curl -s 'https://app.fahrten.fusenigk.de/api/v1/messwerte/verlauf?pid=42' -H "Authorization: Bearer $TOKEN"
Statistics
Key figures and chart data
GET/statistik🔒 Login required
Key figures and chart data
Totals, months, logbook by type, weekdays, times, eco score, trip lengths, consumption, faults by system and "Movement & Parking" (driving time, parking time, ratio, parking breaks).
Parameters
| Name | Location | Type | Description |
|---|
von | query | string · date | |
bis | query | string · date | |
fahrzeug | query | integer | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | Statistics |
| 401 | Not signed in |
curl
curl -s 'https://app.fahrten.fusenigk.de/api/v1/statistik?von=2026-01-01' -H "Authorization: Bearer $TOKEN" | jq '.daten | {summe, bewegung: (.bewegung | {anteilFahren, verhaeltnis})}'
GET/karte/heatmap🔒 Login required
Heatmap: dwell density of all trips (grid cells ≈ 110 m, parking locations weighted more heavily)
Parameters
| Name | Location | Type | Description |
|---|
von | query | string · date | |
bis | query | string · date | |
art | query | string (privat, geschaeftlich, arbeitsweg, none) | |
fahrzeug | query | integer | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | `punkte[[lat, lon, gewicht]]`, `grenzen`, `haeufigsteOrte[]` |
| 401 | Not signed in |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/karte/heatmap -H "Authorization: Bearer $TOKEN" | jq '.daten | {zellen, haeufigsteOrte}'
GET/karte/tage🔒 Login required
Year map: trips and km per day (like the GitHub activity display)
Parameters
| Name | Location | Type | Description |
|---|
jahr | query | integer | |
art | query | string (privat, geschaeftlich, arbeitsweg, none) | |
fahrzeug | query | integer | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | `tage[]`, `aktiveTage`, `laengsteSerie`, `meisteFahrten` |
| 401 | Not signed in |
curl
curl -s 'https://app.fahrten.fusenigk.de/api/v1/karte/tage?jahr=2026' -H "Authorization: Bearer $TOKEN" | jq '.daten | {aktiveTage, laengsteSerie}'
Export
Logbook as CSV or PDF, all data as JSON, route as GPX
GET/fahrten/{id}/gpx🔒 Login required
Route of a trip as GPX 1.1
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | string · uuid | |
Responses
| 200 | GPX file |
| 404 | Not found (or not visible) |
curl
curl -s -OJ https://app.fahrten.fusenigk.de/api/v1/fahrten/$ID/gpx -H "Authorization: Bearer $TOKEN"
GET/export/fahrten.csv🔒 Login required
Logbook as CSV
Semicolon-separated, UTF-8 with BOM (opens directly in Excel/Numbers), with totals per type. Filters like /fahrten.
Parameters
| Name | Location | Type | Description |
|---|
von | query | string · date | |
bis | query | string · date | |
art | query | FahrtArt | |
fahrzeug | query | integer | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | CSV file |
| 401 | Not signed in |
Example response
Datum;Beginn;Ende;Dauer (min);Start;Ziel;km;Art;Notiz;…
06.11.2026;09:43;10:01;18;Unter den Linden 10, 10117 Berlin;Rudower Chaussee 17, 12489 Berlin;17,4;Geschäftlich;Projektbesprechung;…
curl – Business trips 2026 for tax purposes
curl -s -o fahrtenbuch-2026.csv \
'https://app.fahrten.fusenigk.de/api/v1/export/fahrten.csv?art=geschaeftlich&von=2026-01-01&bis=2026-12-31' \
-H "Authorization: Bearer $TOKEN"
GET/export/fahrten.pdf🔒 Login required
Logbook as PDF (A4 landscape)
Print-ready logbook: header with user, period and filter, totals per trip type (private, business, commute, open), table chronologically by months with subtotals, grand total and page numbers. Unicode font (DejaVu Sans), therefore Polish addresses are also correct. Filters like /fahrten.
Parameters
| Name | Location | Type | Description |
|---|
von | query | string · date | |
bis | query | string · date | |
art | query | FahrtArt | |
fahrzeug | query | integer | |
suche | query | string | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | PDF file |
| 401 | Not signed in |
curl – Business trips October as PDF
curl -s -o fahrtenbuch-oktober.pdf \
'https://app.fahrten.fusenigk.de/api/v1/export/fahrten.pdf?art=geschaeftlich&von=2026-10-01&bis=2026-10-31' \
-H "Authorization: Bearer $TOKEN"
GET/export/daten.json🔒 Login required
All own data as JSON
Vehicles, trips, fault codes, readouts and snapshots. With messpunkte=1 additionally all measurement points of each trip.
Parameters
| Name | Location | Type | Description |
|---|
messpunkte | query | string (1) | |
alle | query | string (1) | Administrators only: data of all users |
Responses
| 200 | JSON file |
| 401 | Not signed in |
Example response
{
"benutzer": "max.mustermann",
"format": "obdrest-fahrten-export",
"version": 1,
"fahrzeuge": [],
"fahrten": [],
"fehler": [],
"auslesungen": [],
"momentaufnahmen": []
}
curl
curl -s -o meine-daten.json 'https://app.fahrten.fusenigk.de/api/v1/export/daten.json?messpunkte=1' -H "Authorization: Bearer $TOKEN"
Administration
User management (administrators only)
GET/admin/benutzer🔒 Login required
All users with statistics (admin only)
Responses
| 200 | List |
| 403 | For administrators only |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/admin/benutzer -H "Authorization: Bearer $ADMIN_TOKEN"
POST/admin/benutzer🔒 Login required
Create user (SSO or local)
Request (application/json) – Schema BenutzerEingabe
{
"username": "kasia",
"name": "Katarzyna",
"rolle": "user",
"darfSenden": true,
"darfLoeschen": false,
"aktiv": true,
"anmeldung": "sso"
}
Responses
| 201 | created |
| 409 | already exists |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/admin/benutzer -H "Authorization: Bearer $ADMIN_TOKEN" \
-H 'Content-Type: application/json' -d '{"username":"kasia","rolle":"user","anmeldung":"sso"}'
GET/admin/system🔒 Login required
System status (administrators only)
App and MariaDB version, runtime, memory, rows and size per table, receipt storage and demo data inventory.
Responses
| 200 | OK |
| 401 | Not signed in |
| 403 | administrators only |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/admin/system -H "Authorization: Bearer $TOKEN"
GET/admin/vehicles🔒 Login required
All vehicles with adapter and metrics (administrators only)
Vehicles of all users with log, OBD adapter, number of control units, trips, km, active errors and unknown codes, plus an overview per adapter.
Responses
| 200 | OK |
| 401 | Not signed in |
| 403 | administrators only |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/admin/fahrzeuge -H "Authorization: Bearer $TOKEN"
GET/admin/activity🔒 Login required
Recent activity (administrators only)
Most recently uploaded trips, error readings, measurements, outputs and unknown codes, trips today/7 days and last synchronization per user.
Parameters
| Name | Location | Type | Description |
|---|
limit | query | integer | |
Responses
| 200 | OK |
| 401 | Not signed in |
| 403 | administrators only |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/admin/aktivitaet -H "Authorization: Bearer $TOKEN"
GET/admin/unknown🔒 Login required
Unknown codes of all users with vehicle cross-reference (administrators only)
Parameters
| Name | Location | Type | Description |
|---|
art | query | string (pid, dtc) | |
status | query | string (new, in-analysis, resolved, ignored) | |
fahrzeug | query | integer | |
suche | query | string | code, VIN or vehicle name |
Responses
| 200 | List (new first) with raw data samples, vehicle (VIN, model, control units, mileage) and user |
| 401 | Not signed in |
| 403 | administrators only |
curl
curl -s 'https://app.fahrten.fusenigk.de/api/v1/admin/unbekannt?status=neu' -H "Authorization: Bearer $TOKEN"
PUT/admin/unknown/{id}🔒 Login required
Set analysis status and note (administrators only)
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Request (application/json)
{
"status": "geklaert",
"notiz": "PID 8B = SCR-Dosierung, A = Bitmaske"
}
Responses
| 200 | changed |
| 404 | Entry not found |
| 403 | administrators only |
curl
curl -s -X PUT https://app.fahrten.fusenigk.de/api/v1/admin/unbekannt/1 -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"status":"in-analyse"}'
DELETE/admin/unknown/{id}🔒 Login required
Delete entry (administrators only)
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Responses
| 200 | deleted |
| 404 | Entry not found |
| 403 | administrators only |
curl
curl -s -X DELETE https://app.fahrten.fusenigk.de/api/v1/admin/unbekannt/1 -H "Authorization: Bearer $TOKEN"
GET/admin/benutzer/{id}🔒 Login required
Read individual user
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Responses
| 200 | Users |
| 404 | Not found (or not visible) |
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/admin/benutzer/2 -H "Authorization: Bearer $ADMIN_TOKEN"
PUT/admin/benutzer/{id}🔒 Login required
Change role, permissions, login type, password
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Request (application/json) – Schema BenutzerEingabe
{
"darfLoeschen": true,
"aktiv": true
}
Responses
| 200 | changed |
| 400 | Invalid input |
curl
curl -s -X PUT https://app.fahrten.fusenigk.de/api/v1/admin/benutzer/2 -H "Authorization: Bearer $ADMIN_TOKEN" \
-H 'Content-Type: application/json' -d '{"darfLoeschen":true}'
DELETE/admin/benutzer/{id}🔒 Login required
Delete user with all data
Parameters
| Name | Location | Type | Description |
|---|
id | path · required | integer | |
Responses
| 200 | deleted |
| 400 | Main administrator or own account cannot be deleted |
curl
curl -s -X DELETE https://app.fahrten.fusenigk.de/api/v1/admin/benutzer/7 -H "Authorization: Bearer $ADMIN_TOKEN"
System
Operating status
GET/health🌐 public
Health check incl. database
Responses
| 200 | ok |
| 503 | Database unreachable |
| 429 | Too many requests |
Example response
{
"ok": true,
"daten": {
"status": "ok",
"datenbank": "ok",
"zeit": "2026-11-07T09:00:00.000Z"
}
}
curl
curl -s https://app.fahrten.fusenigk.de/api/v1/health
🧱 Data models
The upload formats correspond to the Codable types of the iOS app – anyone building their own server must accept exactly these fields.
Trip
Trip in the Codable format of the iOS app
| Field (* required) | Type | Description |
|---|
id * | string · uuid | |
startDate * | string · date-time | |
endDate * | string · date-time | |
samples | array of TripSample | |
summary | object | null | TripSummary of the app, incl. distanceKm, maxSpeed, avgSpeed, fuelLiters, litersPer100km, co2Kg, idleSeconds, events[], eco{score} |
startAddress | string | null | |
endAddress | string | null | |
kind | FahrtArt | Logbook assignment; none = not yet assigned |
note | string | |
details | string | null | Multi-line, optional description of the trip |
fuelType | string (diesel, petrol) | |
vin | string | null | |
isDemo | boolean | |
TripSample
Measurement point (approx. 1 per second)
| Field (* required) | Type | Description |
|---|
time * | string · date-time | |
speed | number | null | km/h |
rpm | number | null | |
load | number | null | Engine load % |
coolant | number | null | °C |
voltage | number | null | V |
ambient | number | null | Outside temperature °C |
pedal | number | null | Accelerator pedal % |
fuelRate | number | null | l/h |
latitude | number | null | |
longitude | number | null | |
altitude | number | null | m above sea level |
VehicleUpload
| Field (* required) | Type | Description |
|---|
vin | string | null | |
name | string | null | |
manufacturer | string | null | |
series | string | null | |
modelYear | integer | null | |
fuelType | string | null | |
protocol | string | null | |
adapter | string | null | |
ecus | array of object | |
ErrorUpload
| Field (* required) | Type | Description |
|---|
id * | string · uuid | |
vin | string | null | |
readAt * | string · date-time | |
entries | array of object | |
udsECUs | array of string | |
history | array of object | |
Snapshot
| Field (* required) | Type | Description |
|---|
id * | string · uuid | |
vin | string | null | |
takenAt * | string · date-time | |
voltage | number | null | |
protocol | string | null | |
readings * | array of object | |
findings | array of object | |
Trip
Trip in the list view of the web app
| Field (* required) | Type | Description |
|---|
id | string · uuid | |
benutzer | string | |
fahrzeug | string | null | |
start | string · date-time | |
end | string · date-time | |
durationSeconds | integer | |
km | number | |
maxKmh | number | null | |
avgKmh | number | null | |
litersPer100 | number | null | |
eco | integer | null | |
startAddress | string | null | |
destinationAddress | string | null | |
art | FahrtArt | Logbook assignment; none = not yet assigned |
notiz | string | null | |
beschreibung | string | null | Multi-line description (Trip.details) |
User
| Field (* required) | Type | Description |
|---|
id | integer | |
username | string | |
name | string | null | |
email | string | null | |
role | string (admin, user) | |
isAdmin | boolean | |
maySubmit | boolean | |
mayDelete | boolean | |
aktiv | boolean | |
login | string (sso, local) | |
mayApi | boolean | REST API allowed (app login, Bearer, Basic); otherwise browser session only |
UserInput
| Field (* required) | Type | Description |
|---|
username | string | |
name | string | null | |
email | string | null | |
role | string (admin, user) | |
maySubmit | boolean | |
mayDelete | boolean | |
aktiv | boolean | |
login | string (sso, local) | |
password | string | null | only for login=local |
notiz | string | null | |
mayApi | boolean | |
LoginRequest
| Field (* required) | Type | Description |
|---|
username * | string | |
password * | string · password | |
client | string (app, web) | app = Bearer token 90 days, web = cookie 12 h
|
Registration
| Field (* required) | Type | Description |
|---|
username * | string | |
name * | string | |
email * | string · email | |
password * | string | |
client | string (app, web) | |