{
  "openapi": "3.1.0",
  "info": {
    "title": "aircargobook API",
    "version": "1.0.0",
    "summary": "JSON-API der aircargobook-Plattform — Ratensuche, Frachtenbörse und Angebote.",
    "description": "Über diese API legst du Frachten in der aircargobook-Frachtenbörse an, rufst sie ab,\nsuchst Trucking-Raten und gibst Angebote ab.\n\n## Grundprinzip: `.json` an die URL\n\nJeder Endpunkt liefert JSON, sobald die URL auf `.json` endet. Ohne diese Endung\nbekommst du die HTML-Oberfläche zurück. Das ist die häufigste Fehlerquelle beim\nersten Integrieren:\n\n```\n✗  POST /freights/add          →  HTML\n✓  POST /freights/add.json     →  JSON\n```\n\n## Response-Envelope\n\nAlle JSON-Antworten sind gleich verpackt:\n\n```json\n{\n  \"data\":   { },\n  \"status\": \"error\",\n  \"auth\":   { \"email\": \"…\", \"token\": \"…\", \"language\": \"deu\" }\n}\n```\n\n### ⚠️ Wo die Nutzdaten liegen, ist pro Endpunkt verschieden\n\nEs gibt **keinen** einheitlichen Schlüssel. Verlass dich auf die Tabelle, nicht auf ein\nMuster:\n\n| Endpunkt | Nutzdaten |\n|---|---|\n| `POST /freights/add.json` | **`data`** — die Fracht liegt *flach* in `data`, die ID also unter `data.id` |\n| `POST /freights/edit/{id}.json` | **`data`** (wie beim Anlegen) |\n| `GET /freights/view/{id}.json` | **`data.route`** |\n| `GET /freights/enlist/{id}.json` | **`data.routes`** (Array) |\n| `POST /quotes/add.json` | **`data.bid`** |\n| `POST /pricing/search/results.json` | **`data.rates`**, `data.combinations` |\n\nBeim Anlegen also `data.id` lesen — **nicht** `data.route.id`.\n\n### `status` prüfen\n\n`status` ist `\"success\"`, wenn der Request durchgelaufen ist, und `\"error\"`, wenn nicht.\nAls Gürtel-und-Hosenträger empfiehlt sich, zusätzlich das erwartete Nutzdatenfeld zu\nprüfen — beim Anlegen also `data.id`.\n\n## Formatierte Anzeigefelder (`f_`-Prefix)\n\nNeben den Rohwerten liefern Frachten fertig formatierte Felder für die Anzeige. Die\nkannst du direkt in deine Oberfläche schreiben, statt selbst zu formatieren:\n\n| Feld | Rohwert | formatiert |\n|---|---|---|\n| `f_from` / `f_to` | `FRA` + `DE` | `FRA (DE)` |\n| `f_eta` / `f_etd` | `2026-09-22 14:00:00` | `2026-09-22 14:00` |\n| `f_eta_end` / `f_etd_end` | `null` | `nicht festgelegt` *(in der Sprache des Users)* |\n| `caption` | — | `FRA (DE) to MUC (DE) - (Bestellung 4711)` |\n\nZeitangaben kommen in der Zeitzone der Anwendung und ohne Sekunden. Ist ein Wert nicht\ngesetzt, steht dort ein übersetzter Platzhaltertext statt `null` — praktisch für die\nAnzeige, unbrauchbar zum Rechnen. **Für Logik immer die Rohfelder nehmen, für die\nDarstellung die `f_`-Varianten.**\n\nWeitere Anzeigefelder: `display_id` (Kundenreferenz oder ID), `info`,\n`delivery_status_in_words`, `list_icon`, `media_types`, `editable`.\n\n> `f_from` und `f_to` unterliegen derselben Maskierung wie die Rohfelder: bei Frachten,\n> deren Details du nicht sehen darfst, steht dort `xxx (XX)`.\n\n## Fehlerbehandlung\n\nDie Endpunkte werden auch von der Weboberfläche genutzt. Daraus folgen drei\nEigenheiten, die dein Client behandeln muss:\n\n1. **Behandle `3xx` und HTML-Antworten als Fehler.** Ein fehlgeschlagener Request\n   antwortet nicht immer mit einem `4xx`-JSON — teils kommt ein Redirect auf eine\n   HTML-Seite. Prüfe daher den `Content-Type`: alles außer `application/json` ist ein\n   Fehlschlag. Setze deinen HTTP-Client so, dass er Redirects **nicht** automatisch\n   folgt, sonst siehst du am Ende einen `200` mit HTML-Body.\n\n   Einzige Ausnahme: das Ausschreiben einer Fracht antwortet planmäßig mit `302` und\n   war trotzdem erfolgreich — dort den Zustand per `GET` nachprüfen.\n2. **Prüfe die Antwort gegen das, was du gesendet hast.** Beim Anlegen und Ändern von\n   Frachten werden unbekannte oder falsch typisierte Felder still ignoriert — `status`\n   ist trotzdem `\"success\"`. Ein `eta` ohne Uhrzeit verschwindet zum Beispiel spurlos.\n   Vergleiche die zurückgegebene Ressource mit deinem Request.\n3. **`404` heißt „nicht gefunden *oder* nicht für dich sichtbar\".** Frachten anderer\n   Firmen sind von nicht existierenden nicht unterscheidbar.\n\n## Request-Body\n\nQuery-Parameter, Formularfelder und JSON-Body werden serverseitig zusammengeführt.\nJedes dokumentierte Body-Feld funktioniert daher auch als `application/json`, als\n`application/x-www-form-urlencoded` oder als Query-Parameter. Für neue Integrationen\nist `application/json` die Empfehlung.\n\n## Bitte einen User-Agent senden\n\nSchicke, sofern dein HTTP-Client das zulässt, einen aussagekräftigen `User-Agent`\nim Format `Softwarename / Version`:\n\n```\nUser-Agent: MyApplication / 1.0.0\n```\n\nDas hilft uns, Requests deiner Integration bei Rückfragen und Störungen\nzuzuordnen — ohne ihn sehen wir nur eine anonyme HTTP-Bibliothek.\n\n## Environments\n\nEntwickle immer gegen die Demo-Umgebung. Beide Umgebungen haben getrennte\nDatenbestände und getrennte Zugangsdaten — ein Token aus der Demo funktioniert\n**nicht** in der Produktion.\n\n| Umgebung | Basis-URL |\n|---|---|\n| Demo / Integration | `https://demo.aircargobook.com` |\n| Produktion | `https://www.aircargobook.com` |\n",
    "contact": {
      "name": "aircargobook Support",
      "email": "support@aircargobook.com",
      "url": "https://www.aircargobook.com"
    },
    "x-logo": {
      "url": "https://docs.aircargobook.com/logo.png",
      "altText": "aircargobook.com"
    }
  },
  "servers": [
    {
      "url": "https://demo.aircargobook.com",
      "description": "Demo / Integration — hier entwickeln und testen"
    },
    {
      "url": "https://www.aircargobook.com",
      "description": "Produktion"
    }
  ],
  "security": [
    {
      "authTokenHeader": []
    },
    {
      "authTokenQuery": []
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Anmelden und den API-Token holen, den alle weiteren Requests brauchen.\n"
    },
    {
      "name": "Pricing",
      "description": "Trucking-Raten suchen — Vor- und Nachlauf per LKW zwischen Postleitzahlengebiet\nund Flughafen.\n"
    },
    {
      "name": "Freights",
      "description": "Frachten in der Frachtenbörse anlegen, abrufen und ausschreiben.\n\nIm URL-Segment heißen sie `/freights/…`. Eine Fracht beschreibt eine\nTransportanfrage: Strecke, Gewicht, Packstücke, Zeitfenster.\n"
    },
    {
      "name": "Quotes",
      "description": "Angebote auf Frachten. URL-Segment `/quotes/…`.\n\nEin Angebot gehört immer zu genau einer Fracht. Abgerufen werden Angebote nicht\nhier, sondern über die Fracht selbst — siehe `GET /freights/view/{id}.json`.\n"
    }
  ],
  "paths": {
    "/users/login.json": {
      "post": {
        "tags": [
          "Auth"
        ],
        "operationId": "login",
        "summary": "Anmelden und API-Token holen",
        "description": "Tauscht E-Mail und Passwort gegen deinen API-Token. Der Token steht in der Antwort\nunter `auth.token` und ist der `auth_token` für alle weiteren Requests.\n\nDer Token **rotiert nicht** und hat keine Ablaufzeit — hole ihn einmal, lege ihn\nals Secret ab und verwende ihn wieder. Rufe diesen Endpunkt **nicht** vor jedem\nRequest auf.\n\nDie Felder sind unter `auth` verschachtelt.\n\n> Der Token gilt nur für die Umgebung, in der du ihn geholt hast. Ein Demo-Token\n> funktioniert nicht in der Produktion.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              },
              "example": {
                "auth[user]": "me@example.com",
                "auth[password]": "geheim"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              },
              "example": {
                "auth": {
                  "user": "me@example.com",
                  "password": "geheim"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Login erfolgreich. **Achtung:** Ein `200` mit `status != \"success\"` oder ohne\n`auth.token` bedeutet fehlgeschlagenen Login.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "success"
                    },
                    "auth": {
                      "$ref": "#/components/schemas/Auth"
                    }
                  }
                },
                "example": {
                  "status": "success",
                  "auth": {
                    "email": "me@example.com",
                    "token": "8f14e45fceea167a5a36dedd4bea2543",
                    "language": "deu"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/pricing/search/results.json": {
      "post": {
        "tags": [
          "Pricing"
        ],
        "operationId": "searchRates",
        "summary": "Trucking-Raten suchen",
        "description": "Sucht Trucking-Raten über alle Ratenblätter, die für deine Firma sichtbar sind.\nDein Benutzer muss dafür einer Firma zugeordnet sein.\n\nEs werden bis zu **200** direkte Raten geliefert. `combinations` enthält\nzusätzlich gefundene Multi-Leg-Kombinationen.\n\nJede Suche wird gespeichert. Ihre ID steht in `data.searchEntity.id` und lässt sich\nspäter als `pricing_search_id` an\n[`POST /freights/add.json`](#tag/freights/post/freights~1add.json) übergeben, um\neine Fracht daraus vorzubefüllen.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RateSearchRequest"
              },
              "examples": {
                "gewicht": {
                  "summary": "Gewichtssuche City → Airport",
                  "value": {
                    "origin": "city:D-63000",
                    "destination": "airport:FRA",
                    "weight": 250,
                    "direction": "auto",
                    "allow_combinations": 1
                  }
                },
                "packstuecke": {
                  "summary": "Suche mit Packstückliste",
                  "value": {
                    "origin": "airport:FRA",
                    "destination": "city:D-80331",
                    "pieces_json": [
                      {
                        "pieces": 2,
                        "depth": 120,
                        "width": 80,
                        "height": 100,
                        "weight": 125
                      }
                    ],
                    "unitOfLength": "cm"
                  }
                },
                "uld": {
                  "summary": "ULD-Suche",
                  "value": {
                    "origin": "airport:FRA",
                    "destination": "airport:MUC",
                    "search_type": "uld",
                    "uld_count": 3
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/RateSearchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suchergebnis.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/RateSearchResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "get": {
        "tags": [
          "Pricing"
        ],
        "operationId": "rerunSearch",
        "summary": "Gespeicherte Suche erneut ausführen",
        "description": "Führt eine Suche aus der Historie erneut mit aktuellen Raten aus.",
        "parameters": [
          {
            "name": "search_id",
            "in": "query",
            "required": true,
            "description": "ID einer gespeicherten Suche (`data.searchEntity.id` einer früheren Suche).",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suchergebnis.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/RateSearchResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/pricing/public-search/{companyId}/results.json": {
      "post": {
        "tags": [
          "Pricing"
        ],
        "operationId": "publicSearchRates",
        "summary": "Öffentliche Ratensuche (ohne Login)",
        "description": "Sucht ausschließlich in Rate-Sheets der angegebenen Company, die die Sichtbarkeit\n`public_search` haben. Kein Token nötig — der einzige unauthentifizierte\nPricing-Endpunkt.\n\nGleiche Body-Felder wie die authentifizierte Suche.\n",
        "security": [],
        "parameters": [
          {
            "name": "companyId",
            "in": "path",
            "required": true,
            "description": "ID der Company, deren öffentliche Sheets durchsucht werden.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RateSearchRequest"
              },
              "example": {
                "origin": "airport:FRA",
                "destination": "city:D-80331",
                "weight": 100
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suchergebnis (nur öffentliche Sheets der Company).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/RateSearchResult"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/pricing/search/history.json": {
      "get": {
        "tags": [
          "Pricing"
        ],
        "operationId": "searchHistory",
        "summary": "Suchhistorie",
        "description": "Liefert die gespeicherten Suchen der eigenen Company.",
        "responses": {
          "200": {
            "description": "Liste vergangener Suchen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/pricing/search/location/{query}/{type}.json": {
      "get": {
        "tags": [
          "Pricing"
        ],
        "operationId": "locationAutocomplete",
        "summary": "Ort-Autocomplete",
        "description": "Löst eine Sucheingabe zu Airports oder Städten/PLZ auf. Die zurückgegebenen Werte\nsind direkt als `origin` / `destination` verwendbar (Format `airport:FRA` bzw.\n`city:D-63000`).\n",
        "parameters": [
          {
            "name": "query",
            "in": "path",
            "required": true,
            "description": "Suchbegriff (min. 2 Zeichen), URL-encoded.",
            "schema": {
              "type": "string"
            },
            "example": "frank"
          },
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "airport",
                "city"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Treffer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/pricing/search/rate-search.json": {
      "post": {
        "tags": [
          "Pricing"
        ],
        "operationId": "rateSearchResult",
        "summary": "Suchergebnis bewerten (Daumen hoch/runter)",
        "description": "> ⚠️ **Namensfalle:** Dieser Endpunkt ist **nicht** die Ratensuche, sondern das\n> Feedback zu einem Suchergebnis. Die Suche ist\n> `POST /pricing/search/results.json`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "search_id",
                  "rating"
                ],
                "properties": {
                  "search_id": {
                    "type": "integer",
                    "description": "ID der Suche."
                  },
                  "rating": {
                    "type": "integer",
                    "enum": [
                      0,
                      1
                    ],
                    "description": "`1` = Daumen hoch, `0` = Daumen runter."
                  },
                  "rate_id": {
                    "type": "integer",
                    "description": "Optional die konkret bewertete Rate."
                  }
                }
              },
              "example": {
                "search_id": 987,
                "rating": 1,
                "rate_id": 123
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bewertung gespeichert.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/freights/add.json": {
      "post": {
        "tags": [
          "Freights"
        ],
        "operationId": "createFreight",
        "summary": "Fracht anlegen",
        "description": "Legt eine neue Fracht an.\n\n### Wem gehört die Fracht?\n\nImmer der Firma, zu der dein Benutzer gehört. `company` und `user` werden\nserverseitig gesetzt und lassen sich nicht überschreiben — es gibt **keinen**\nParameter, um im Namen einer anderen Firma anzulegen, und keinen anonymen\nAnlage-Endpunkt.\n\nWenn deine Integration Frachten für mehrere Firmen erzeugen soll, brauchst du pro\nFirma einen eigenen Benutzer und damit einen eigenen Token.\n\n### Status nach dem Anlegen\n\n| Bedingung | resultierender `status` |\n|---|---|\n| Standardfall | `draft` |\n| `winner_direct` gesetzt | `open` (+ `visibility = -2`, `whitelist = [winner_direct]`) |\n| `refs.obj_id` gesetzt | `draft` |\n\n**Eine `draft`-Fracht ist für Anbieter nicht sichtbar.** Zum Ausschreiben\nanschließend `POST /freights/view/{id}.json` mit `{\"action\": \"publish\"}` aufrufen.\nDas ist der Schritt, der am häufigsten fehlt.\n\n### Start und Ziel\n\n`from` und `to` akzeptieren drei Schreibweisen: `airport:FRA`, `city:34119` oder den\nnackten IATA-Code `FRA`. Der Prefix wird abgeschnitten.\n\nBei einem **IATA-Code** werden Adresse, Land und Koordinaten automatisch ergänzt —\nallerdings nur, solange `from_addr` bzw. `to_addr` leer sind. Bei `city:`-Werten\nfindet keine automatische Auflösung statt.\n",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Frachttyp. Default `AHR`.\n",
            "schema": {
              "$ref": "#/components/schemas/FreightType"
            }
          },
          {
            "name": "pricing_search_id",
            "in": "query",
            "description": "Vorbefüllung aus einer Ratensuche: übernimmt `from`, `to`, `weight` und\nPackstücke und hinterlegt `refs.pricing_search_id`.\n",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FreightCreateRequest"
              },
              "examples": {
                "trucking_vollstaendig": {
                  "summary": "Trucking-Ausschreibung — vollständig (empfohlener Einstieg)",
                  "description": "Enthält alles, was die Weboberfläche beim Anlegen einer Ausschreibung\nmitschickt. So sieht die Fracht auch im UI vollständig aus.\n\nWeil Start und Ziel Postleitzahlen sind, löst der Server Land, Adresse\nund Koordinaten **nicht** auf — deshalb sind die `from_*` / `to_*`-Felder\nhier explizit gesetzt.\n\nErgebnis: `status: \"draft\"` — danach noch ausschreiben über\n`POST /freights/view/{id}.json` mit `{\"action\": \"publish\"}`.\n",
                  "value": {
                    "class": "rfs",
                    "type": "AHR",
                    "category": "ftl",
                    "from": "city:60547",
                    "from_country": "DE",
                    "from_addr": "Frankfurt am Main, Germany",
                    "from_latitude": 50.0379,
                    "from_longitude": 8.5622,
                    "to": "city:34119",
                    "to_country": "DE",
                    "to_addr": "Kassel, Germany",
                    "to_latitude": 51.3127,
                    "to_longitude": 9.4797,
                    "eta": "2026-09-22 14:00:00",
                    "dangerous": "no",
                    "secured": 1,
                    "pieces": [
                      {
                        "pieces": 2,
                        "depth": 120,
                        "width": 80,
                        "height": 100,
                        "weight": 125,
                        "stackable": 1
                      },
                      {
                        "pieces": 2,
                        "depth": 120,
                        "width": 80,
                        "height": 60,
                        "weight": 115,
                        "stackable": 1
                      }
                    ],
                    "refs": {
                      "commodity": "Autoteile",
                      "customer": "Musterkunde GmbH / Bestellung 4711",
                      "incoterm": "dap",
                      "incoterm_place": "Kassel",
                      "pieces": 4,
                      "weight_actual": 480,
                      "weight_volume": 640,
                      "capacity": 3.84,
                      "external_ref": "MYAPP-2026-00123"
                    }
                  }
                },
                "trucking_minimal": {
                  "summary": "Trucking-Ausschreibung — Minimum",
                  "description": "Das Wenigste, mit dem eine brauchbare Ausschreibung entsteht. Ohne die\n`*_addr`-Felder bleiben Adresse und Koordinaten leer — für eine reine\nPLZ-zu-PLZ-Anfrage reicht das.\n",
                  "value": {
                    "class": "rfs",
                    "from": "city:60547",
                    "to": "city:34119",
                    "eta": "2026-09-22 14:00:00",
                    "refs": {
                      "weight_actual": 480
                    },
                    "pieces": [
                      {
                        "pieces": 4,
                        "depth": 120,
                        "width": 80,
                        "height": 100,
                        "weight": 120
                      }
                    ]
                  }
                },
                "luftfracht": {
                  "summary": "Luftfracht-Ausschreibung",
                  "description": "Bei IATA-Codes ergänzt der Server Land, Adresse und Koordinaten selbst —\ndie `from_*` / `to_*`-Felder können entfallen.\n",
                  "value": {
                    "class": "air",
                    "type": "AHR",
                    "from": "airport:FRA",
                    "to": "airport:JFK",
                    "direction": "export",
                    "weight_actual": 480,
                    "weight_volume": 640,
                    "etd_range": "2026-09-20 08:00 to 2026-09-20 18:00",
                    "dangerous": "no",
                    "secured": 1,
                    "pieces": [
                      {
                        "pieces": 4,
                        "depth": 120,
                        "width": 80,
                        "height": 100,
                        "weight": 120,
                        "stackable": 1
                      }
                    ],
                    "refs": {
                      "commodity": "Autoteile",
                      "customer": "Musterkunde GmbH / Bestellung 4711"
                    }
                  }
                },
                "direktbuchung": {
                  "summary": "Direktbuchung an einen festen Anbieter (DTO)",
                  "description": "Mit `?type=DTO` aufrufen. `winner_direct` setzt in einem Schritt\n`whitelist = [42]`, `visibility = -2` und `status = open` — die Fracht ist\nsofort scharf und nur für Firma 42 sichtbar. Kein separates Ausschreiben\nnötig.\n",
                  "value": {
                    "class": "rfs",
                    "from": "city:60547",
                    "to": "city:34119",
                    "eta": "2026-09-22 14:00:00",
                    "winner_direct": 42,
                    "price": 780,
                    "refs": {
                      "customer": "Musterkunde GmbH / Bestellung 4711",
                      "external_ref": "MYAPP-2026-00123"
                    }
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/FreightCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fracht angelegt.\n\n**Die Fracht liegt flach in `data`** — die neue ID also unter `data.id`, nicht\nunter `data.route.id`. Beim Abrufen ist es dann `data.route`. Im UI erreichbar\nunter `/freights/view/{id}`.\n\nNeben den Rohfeldern liefert die Antwort formatierte Anzeigefelder (`f_from`,\n`f_eta`, `caption`, `display_id`, …), die du direkt in deine Oberfläche schreiben\nkannst.\n",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Freight"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "data": {
                    "id": 1965,
                    "class": "rfs",
                    "type": "AHR",
                    "status": "draft",
                    "category": "ftl",
                    "target": 3,
                    "from": "FRA",
                    "to": "34119",
                    "from_country": "DE",
                    "from_addr": "Frankfurt,Germany",
                    "from_latitude": 50.049,
                    "from_longitude": 8.57368,
                    "eta": null,
                    "dangerous": "no",
                    "dangerous_goods": "no",
                    "secured": "1",
                    "company": 101,
                    "user": 1001038,
                    "created": "2026-09-15 13:42:32",
                    "modified": "2026-09-15 13:42:32",
                    "refs": {
                      "commodity": "Autoteile",
                      "customer": "Musterkunde GmbH / Bestellung 4711",
                      "incoterm": "dap",
                      "incoterm_place": "Kassel",
                      "pieces": 4,
                      "weight_actual": 480,
                      "weight_volume": 640,
                      "capacity": 3.84,
                      "external_ref": "MYAPP-2026-00123"
                    },
                    "f_from": "60547 (DE)",
                    "f_to": "34119 (DE)",
                    "f_eta": "2026-09-22 14:00",
                    "f_etd": "nicht festgelegt"
                  },
                  "status": "success",
                  "auth": {
                    "email": "me@example.com",
                    "token": "8f14e45fceea167a5a36dedd4bea2543",
                    "language": "deu"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/freights/view/{id}.json": {
      "get": {
        "tags": [
          "Freights"
        ],
        "operationId": "getFreight",
        "summary": "Fracht abrufen",
        "description": "Liefert eine Fracht mit ihren Packstücken und — unter zwei Bedingungen — den\nAngeboten dazu.\n\n### Hier bekommst du die Angebote\n\nEs gibt **keinen** eigenen Endpunkt, um Angebote zu einer Fracht abzurufen.\nAngebote kommen als Array `data.route._bids` in dieser Antwort mit. Zwei\nBedingungen müssen erfüllt sein:\n\n1. Die Fracht hat den Status `open`, `pending` oder `taken`. Bei `draft`,\n   `expired`, `revoked`, `closed` oder `archived` fehlt `_bids` ganz.\n2. **Sichtbarkeit:** Gehört die Fracht deiner Firma, siehst du *alle* Angebote.\n   Gehört sie einer anderen Firma, siehst du ausschließlich *deine eigenen*\n   Angebote — die der Mitbewerber werden herausgefiltert.\n\nWenn `_bids` fehlt, heißt das also nicht zwingend „keine Angebote vorhanden\".\nPrüfe zuerst den Status.\n\n### Sichtbarkeit allgemein\n\nWas du siehst, hängt von deiner Rolle an der Fracht ab (Eigentümer, Bieter,\nbeauftragter Transporteur). Bei fremden Frachten können einzelne Felder maskiert\nsein.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FreightId"
          }
        ],
        "responses": {
          "200": {
            "description": "Fracht.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "route": {
                              "$ref": "#/components/schemas/Freight"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Freights"
        ],
        "operationId": "publishFreight",
        "summary": "Fracht ausschreiben (veröffentlichen)",
        "description": "Schreibt eine Fracht aus und setzt damit `status = open`. Erst danach sehen Anbieter\ndie Fracht und können Angebote abgeben.\n\n> **Achtung, ungewöhnlicher Pfad:** Es gibt keinen `/freights/publish/`-Endpunkt.\n> Ausgeschrieben wird über **dieselbe URL wie beim Abrufen**, per `POST` und mit\n> `{\"action\": \"publish\"}` im Body. Ein `POST /freights/publish/{id}.json` läuft ins\n> Leere.\n\nOhne `deadline` wird automatisch **jetzt + 24 Stunden** gesetzt, damit immer ein\nBietfenster existiert.\n\nAusschreiben darf nur die Firma, der die Fracht gehört. Andernfalls kommt ein\nRedirect auf die Fracht-Seite statt eines Fehler-JSON — als Fehler behandeln.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FreightId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "const": "publish"
                  },
                  "visibility": {
                    "$ref": "#/components/schemas/Visibility"
                  },
                  "whitelist": {
                    "type": "array",
                    "description": "Company-IDs, die die Fracht sehen dürfen. Wird **nur** übernommen,\nwenn gleichzeitig `visibility = -2` gesetzt ist.\n",
                    "items": {
                      "type": "integer"
                    }
                  },
                  "deadline": {
                    "type": "string",
                    "description": "Ende des Bietfensters als `YYYY-MM-DD HH:MM`. Relative Angaben wie\n`+48 hours` werden ebenfalls akzeptiert.\n\nFehlt der Wert und die Fracht hat noch keine Deadline, wird\nautomatisch **jetzt + 24 Stunden** gesetzt.\n"
                  }
                }
              },
              "examples": {
                "oeffentlich": {
                  "summary": "Öffentlich publizieren, Deadline +48h",
                  "value": {
                    "action": "publish",
                    "deadline": "+48 hours"
                  }
                },
                "geschlossen": {
                  "summary": "Nur an ausgewählte Anbieter",
                  "value": {
                    "action": "publish",
                    "visibility": -2,
                    "whitelist": [
                      42,
                      88,
                      130
                    ],
                    "deadline": "2026-09-19 17:00"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Publiziert.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "route": {
                              "$ref": "#/components/schemas/Freight"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "302": {
            "description": "Die Fracht gehört nicht deiner Firma. Statt eines Fehler-JSON kommt ein Redirect\nauf die Fracht-Seite — als Fehler behandeln.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/freights/edit/{id}.json": {
      "post": {
        "tags": [
          "Freights"
        ],
        "operationId": "updateFreight",
        "summary": "Fracht bearbeiten",
        "description": "Patcht eine bestehende Fracht. Gleiche Feldsemantik wie beim Anlegen; nur\nmitgesendete Felder werden geändert.\n\nSinnvoll vor allem auf `draft`-Frachten. Nach dem Publizieren sollten\nKerndaten (Route, Gewicht, Zeitfenster) nicht mehr geändert werden — bereits\nabgegebene Angebote beziehen sich darauf.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/FreightId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FreightCreateRequest"
              },
              "example": {
                "weight": 320,
                "etd_range": "2026-09-21 06:00 to 2026-09-21 12:00"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Aktualisierte Fracht.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "route": {
                              "$ref": "#/components/schemas/Freight"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/freights/enlist/{filterId}.json": {
      "get": {
        "tags": [
          "Freights"
        ],
        "operationId": "listFreights",
        "summary": "Frachten listen",
        "description": "Listet Frachten nach einer vordefinierten Filter-ID — das sind dieselben Listen,\ndie im UI im Frachten-Dashboard als Reiter erscheinen. Es gibt **keinen** freien\nQuery-Filter.\n\nDas Ergebnis ist paginiert über `page`, `limit`, `sort` und `direction`.\n**Die Antwort enthält keine Pagination-Metadaten** — weder Gesamtzahl noch\nSeitenanzahl. Blättere, bis das zurückgegebene Array kürzer als `limit` ist.\n",
        "parameters": [
          {
            "name": "filterId",
            "in": "path",
            "required": true,
            "description": "Vordefinierte Liste. Die relevanten IDs für die Frachtenbörse:\n\n**Als Anbieter (fremde Frachten):**\n\n| ID | Liste |\n|----|-------|\n| `8` | Offene Anfragen, auf die ich noch **nicht** geboten habe |\n| `7` | Anfragen, auf die ich geboten habe |\n| `6` | Anfragen, bei denen mein Angebot abgelehnt wurde |\n| `2` | Gewonnene Aufträge (`status = taken`, Lieferung offen) |\n| `9` | Archiv fremder Frachten (`archived`, `closed`, `revoked`, `expired`) |\n\n**Als Auftraggeber (eigene Frachten):**\n\n| ID | Liste |\n|----|-------|\n| `20` | Meine Entwürfe (`status = draft`) |\n| `10` | Meine offenen Tender (`open`, `pending`) |\n| `17` | Meine Tender **mit** eingegangenen Angeboten |\n| `18` | Meine offenen Tender (Basis für „ohne Angebote\") |\n| `12` | Meine beauftragten Transporte (`taken`) |\n| `19` | Archiv eigener Frachten |\n\n**Buchungsanfragen (FFR):** `200`/`201`/`209` ausgehend (offen/erfolgreich/Archiv),\n`210`/`211`/`219` eingehend.\n",
            "schema": {
              "type": "integer"
            },
            "example": 20
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sortierfeld, z. B. `Routes.created`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liste von Frachten unter `data.routes`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "routes": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Freight"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/freights/request/{entity}/{objId}.json": {
      "post": {
        "tags": [
          "Freights"
        ],
        "operationId": "requestFromRate",
        "summary": "Buchungsanfrage aus Rate oder Flight",
        "description": "Erzeugt eine Buchungsanfrage (`type = FFR`, `class = air`) zu einer konkreten\nveröffentlichten Rate oder einem Kapazitätsflug. `from` / `to` / `etd` / `eta`\nwerden aus dem Objekt übernommen, `refs.entity` und `refs.obj_id` gesetzt.\n\nBei `entity = flight` wird der Anbieter automatisch aus dem Flug übernommen und\nals `winner` gesetzt.\n",
        "parameters": [
          {
            "name": "entity",
            "in": "path",
            "required": true,
            "description": "- `rate` — eine veröffentlichte Luftfrachtrate\n- `flight` — ein einzelner Kapazitätsflug\n- `direct` — direkte Anfrage ohne Bezug auf Rate oder Flug\n",
            "schema": {
              "type": "string",
              "enum": [
                "rate",
                "flight",
                "direct"
              ]
            }
          },
          {
            "name": "objId",
            "in": "path",
            "required": true,
            "description": "ID des Rate- bzw. Flight-Objekts.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FreightCreateRequest"
              },
              "example": {
                "weight": 480,
                "pieces": [
                  {
                    "pieces": 4,
                    "depth": 120,
                    "width": 80,
                    "height": 90,
                    "weight": 120
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Buchungsanfrage angelegt.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "route": {
                              "$ref": "#/components/schemas/Freight"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/freights/create-from-template.json": {
      "post": {
        "tags": [
          "Freights"
        ],
        "operationId": "createFreightFromTemplate",
        "summary": "Fracht aus Vorlage anlegen",
        "description": "Klont die Quell-Fracht einer `RouteTemplate` der eigenen Company. Die neue Fracht\nstartet direkt mit `status = taken` und die eigene Company als Transporteur —\ndas ist ein Charter-/TMS-Flow, **keine** Ausschreibung.\n\nDie Vorlage muss der eigenen Company gehören.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "template_id"
                ],
                "properties": {
                  "template_id": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "template_id": 17
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Neue Fracht-ID unter `data.route_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "route_id": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/freights/track/{query}.json": {
      "get": {
        "tags": [
          "Freights"
        ],
        "operationId": "trackFreight",
        "summary": "Sendung verfolgen (öffentlich)",
        "description": "Der einzige Frachten-Endpunkt ohne Authentifizierung\n(`allowUnauthenticated(['track'])`).\n",
        "security": [],
        "parameters": [
          {
            "name": "query",
            "in": "path",
            "required": true,
            "description": "AWB-Nummer oder Referenz, URL-encoded.",
            "schema": {
              "type": "string"
            },
            "example": "020-12345675"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "awb"
            }
          },
          {
            "name": "fullstring",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tracking-Ergebnis.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/quotes/add.json": {
      "post": {
        "tags": [
          "Quotes"
        ],
        "operationId": "createQuote",
        "summary": "Angebot abgeben oder aktualisieren",
        "description": "Gibt ein Angebot (`Bid`) auf eine Fracht ab. Mit gesetzter `id` wird ein\nbestehendes Angebot aktualisiert, ohne `id` ein neues mit `status = open` erzeugt.\n\nAutomatisch gesetzt: `company` und `user` aus dem Token-User,\n`display_company_id` (falls leer). Bietet die Company der Fracht selbst,\nwird `type = selling`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "neu": {
                  "summary": "Neues Angebot",
                  "value": {
                    "route": 12345,
                    "amount": 1875.5,
                    "currency_code": "EUR",
                    "rate_base": 1500,
                    "rate_fsc": 275.5,
                    "rate_ssc": 100,
                    "note": "Direktflug, Abflug wie angefragt"
                  }
                },
                "update": {
                  "summary": "Bestehendes Angebot nachbessern",
                  "value": {
                    "id": 9911,
                    "route": 12345,
                    "amount": 1790
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Angebot gespeichert.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/quotes/confirm/{bidId}.json": {
      "post": {
        "tags": [
          "Quotes"
        ],
        "operationId": "confirmQuote",
        "summary": "Angebot bestätigen / Zuschlag erteilen",
        "description": "Zweistufige Bestätigung: jede Seite bestätigt einmal. Erst wenn **beide** Seiten\nbestätigt haben, wird die Fracht auf `status = taken` gesetzt, `winner` und\n`transport_company_id` auf die Bieter-Company, `route.price` auf den Angebotsbetrag —\nund alle anderen `pending`-Angebote fallen auf `open` zurück.\n\n> Dieser Endpunkt antwortet je nach Bestätigungsstufe mit einem Redirect statt mit\n> verwertbarem JSON. Verifiziere den Zustand danach über\n> `GET /freights/view/{routeId}.json` und prüfe `status` sowie `winner`, statt dem\n> Response-Body zu vertrauen.\n",
        "parameters": [
          {
            "name": "bidId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bestätigung verarbeitet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "302": {
            "description": "Erste Bestätigungsstufe oder fehlende Berechtigung — Redirect. Zustand\nseparat prüfen.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/quotes/revoke/{bidId}.json": {
      "post": {
        "tags": [
          "Quotes"
        ],
        "operationId": "revokeQuote",
        "summary": "Angebot zurückziehen",
        "description": "Zieht ein eigenes Angebot zurück (`status = revoked`). Danach zählt es in den\nFrachtlisten nicht mehr als abgegebenes Angebot — die Fracht erscheint für die\neigene Company wieder unter „noch nicht geboten\" (Filter `8`).\n",
        "parameters": [
          {
            "name": "bidId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Angebot zurückgezogen.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "authTokenHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "**Empfohlen.** Dein API-Token als Header. Das Schema heißt `ApplePass` —\nhistorisch bedingt, aber so muss es lauten:\n\n```\nAuthorization: ApplePass DEIN_TOKEN\n```\n\nDen Token holst du über\n[`POST /users/login.json`](#tag/auth/post/users~1login.json).\n"
      },
      "authTokenQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "auth_token",
        "description": "Derselbe Token als Query-Parameter: `?auth_token=DEIN_TOKEN`.\n\nPraktisch für `curl` und schnelle Tests.\n\n> Nicht für produktive Integrationen verwenden: Query-Parameter landen in\n> Server-Logs, Proxy-Logs und Browser-Referrern. Nutze dort `authTokenHeader`.\n"
      }
    },
    "parameters": {
      "FreightId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Fracht-ID (`routes.id`).",
        "schema": {
          "type": "integer"
        },
        "example": 12345
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Token fehlt, ist ungültig oder gehört zu einer anderen Umgebung.\n",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "string",
                  "const": "error"
                }
              }
            },
            "example": {
              "status": "error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Datensatz existiert nicht oder ist für die eigene Company nicht sichtbar.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "data": {
                  "type": "array",
                  "maxItems": 0
                },
                "status": {
                  "type": "string"
                }
              }
            },
            "example": {
              "data": [],
              "status": "error"
            }
          }
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "title": "Response-Envelope",
        "description": "Gemeinsame Hülle aller JSON-Antworten.",
        "properties": {
          "data": {
            "type": "object",
            "description": "View-Variablen der Action."
          },
          "status": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ]
          },
          "auth": {
            "$ref": "#/components/schemas/Auth"
          }
        }
      },
      "Auth": {
        "type": "object",
        "title": "Auth-Block",
        "properties": {
          "email": {
            "type": "string"
          },
          "token": {
            "type": "string",
            "description": "Dein API-Token. Genau dieser Wert ist der `auth_token` für alle weiteren\nRequests.\n"
          },
          "language": {
            "type": "string",
            "description": "Sprachcode, 3-stellig.",
            "examples": [
              "deu",
              "eng"
            ]
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "title": "Login",
        "required": [
          "auth"
        ],
        "properties": {
          "auth": {
            "type": "object",
            "required": [
              "user",
              "password"
            ],
            "properties": {
              "user": {
                "type": "string",
                "description": "E-Mail oder Username."
              },
              "password": {
                "type": "string",
                "format": "password"
              }
            }
          }
        }
      },
      "FreightType": {
        "type": "string",
        "title": "Frachttyp",
        "default": "AHR",
        "enum": [
          "AHR",
          "FFR",
          "DTR",
          "DTO"
        ],
        "description": "| Wert | Bedeutung | Verwendung |\n|------|-----------|------------|\n| `AHR` | Ausschreibung / Tender | Default. Anfrage an mehrere Anbieter, Angebote (Bids) kommen zurück. |\n| `FFR` | Buchungsanfrage | Anfrage zu einer konkreten Rate oder Kapazität. Startet als `draft`. |\n| `DTR` | Direkte Anfrage | Anfrage an einen bestimmten Anbieter. |\n| `DTO` | Direkte Beauftragung | Direktbuchung an festen Anbieter zu festem Preis. |\n"
      },
      "FreightStatus": {
        "type": "string",
        "title": "Frachtstatus",
        "enum": [
          "draft",
          "open",
          "pending",
          "taken",
          "expired",
          "revoked",
          "closed",
          "archived"
        ],
        "description": "- `draft` — Entwurf, für Anbieter **nicht** sichtbar\n- `open` — publiziert, Angebote möglich\n- `pending` — Angebot angenommen, wartet auf Gegenbestätigung\n- `taken` — beauftragt, `winner` gesetzt\n- `expired` — Deadline verstrichen\n- `revoked` — zurückgezogen\n- `closed` / `archived` — abgeschlossen\n"
      },
      "Visibility": {
        "type": "integer",
        "title": "Sichtbarkeit",
        "description": "`-2` = nur die Companys in `whitelist`. Andere Werte öffnen die Fracht für\nbreitere Empfängerkreise.\n",
        "examples": [
          -2
        ]
      },
      "TransportClass": {
        "type": "string",
        "title": "Transportart",
        "default": "rfs",
        "enum": [
          "rfs",
          "air",
          "rfs_order"
        ],
        "description": "Ob gefahren oder geflogen wird.\n\n| Wert | Bedeutung | `target` |\n|---|---|---|\n| `rfs` | **Trucking** (Road Feeder Service). Der Normalfall — Vor-, Nach- und Zulauf per LKW. | `3` |\n| `air` | **Luftfracht.** Flughafen zu Flughafen. | `2` |\n| `rfs_order` | Trucking als Direktauftrag statt Ausschreibung. | `3` |\n\n`target` wird daraus abgeleitet und serverseitig gesetzt — nicht mitsenden.\n\nFür Trucking (`rfs`) genügen bei `from` / `to` Postleitzahlen; Flughafencodes sind\ndort ebenfalls erlaubt, etwa für Zuläufe zum Flughafen.\n"
      },
      "DangerousGoods": {
        "type": "string",
        "title": "Gefahrgut",
        "enum": [
          "no",
          "1000_less",
          "1000_plus",
          "pax",
          "cao"
        ],
        "description": "Wird auf `dangerous_goods` gemappt:\n\n| Eingabe | gespeichert |\n|---|---|\n| `no` | `no` |\n| `1000_less` | `<1000` |\n| `1000_plus` | `>1000` |\n| `pax` | `pax` |\n| `cao` | `cao` |\n"
      },
      "Piece": {
        "type": "object",
        "title": "Packstück",
        "description": "Eine Zeile der Packstückliste. Maße in **cm**, Gewicht in **kg**.\n\nEine Zeile beschreibt *n* gleiche Packstücke: `pieces: 2` mit `weight: 125` sind\nzwei Packstücke von je 125 kg (bzw. 125 kg zusammen — siehe `weight_per_piece`).\n",
        "properties": {
          "pieces": {
            "type": "integer",
            "default": 1,
            "description": "Anzahl identischer Packstücke in dieser Zeile."
          },
          "depth": {
            "type": "number",
            "description": "Länge in cm."
          },
          "width": {
            "type": "number",
            "description": "Breite in cm."
          },
          "height": {
            "type": "number",
            "description": "Höhe in cm."
          },
          "weight": {
            "type": "number",
            "description": "Gewicht in kg. Ob das je Packstück oder für die ganze Zeile gilt, steuert\n`weight_per_piece`.\n"
          },
          "weight_per_piece": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "default": 0,
            "description": "`1` = `weight` gilt **je Packstück**, `0` = `weight` ist das Gesamtgewicht\ndieser Zeile.\n"
          },
          "stackable": {
            "type": "integer",
            "enum": [
              0,
              1
            ],
            "default": 1,
            "description": "Stapelbar."
          },
          "refs": {
            "type": "object",
            "description": "Kennungen am einzelnen Packstück — `awb`, `uld` oder `other`.\n",
            "additionalProperties": true,
            "properties": {
              "awb": {
                "type": "string",
                "description": "Air Waybill dieses Packstücks (Format `123-45678901`)."
              },
              "uld": {
                "type": "string",
                "description": "ULD-Kennung."
              },
              "other": {
                "type": "string",
                "description": "Freie Kennung."
              }
            }
          }
        },
        "example": {
          "pieces": 2,
          "depth": 120,
          "width": 80,
          "height": 100,
          "weight": 125,
          "stackable": 1
        }
      },
      "FreightCreateRequest": {
        "type": "object",
        "title": "Fracht anlegen / bearbeiten",
        "description": "**Unbekannte oder falsch typisierte Felder werden still verworfen** — es gibt keine\nSchema-Validierung. Vergleiche die zurückgegebene Fracht mit deinem Request.\n\n`company`, `user` und `target` werden serverseitig gesetzt und dürfen nicht\nmitgesendet werden.\n",
        "properties": {
          "from": {
            "type": "string",
            "description": "Start. Drei Schreibweisen:\n\n| Wert | Bedeutung |\n|---|---|\n| `airport:FRA` | Flughafen per IATA-Code |\n| `FRA` | dasselbe, ohne Prefix |\n| `city:34119` | Postleitzahlengebiet — für Trucking der Normalfall |\n\nDer Prefix wird abgeschnitten und nicht gespeichert; in der Antwort steht nur\nnoch `34119` bzw. `FRA`.\n\n**Bei IATA-Codes** löst der Server `from_country`, `from_addr` und die\nKoordinaten automatisch auf — allerdings nur, solange `from_addr` leer ist.\n\n**Bei Postleitzahlen findet keine Auflösung statt.** Wenn du Land, Adresse und\nKoordinaten brauchst, sende sie über die Felder unten selbst mit.\n",
            "examples": [
              "city:34119",
              "airport:FRA",
              "FRA"
            ]
          },
          "to": {
            "type": "string",
            "description": "Ziel, gleiche Kodierung wie `from`.",
            "examples": [
              "city:60547",
              "airport:JFK"
            ]
          },
          "from_country": {
            "type": "string",
            "description": "ISO-Ländercode des Starts. Bei IATA-Codes automatisch gesetzt, bei\nPostleitzahlen **nicht** — dann selbst mitsenden.\n",
            "examples": [
              "DE"
            ]
          },
          "from_addr": {
            "type": "string",
            "description": "Klartext-Adresse des Starts. Bei IATA-Codes automatisch als `Stadt,Land`\ngesetzt, bei Postleitzahlen nicht.\n\n⚠️ Ist dieses Feld gesetzt, unterbleibt die automatische IATA-Auflösung\nkomplett — du übernimmst dann die Verantwortung für alle vier `from_*`-Felder.\n",
            "examples": [
              "Kassel, Germany"
            ]
          },
          "from_latitude": {
            "type": "number",
            "description": "Breitengrad des Starts. Bei Postleitzahlen selbst mitsenden.",
            "examples": [
              51.3127
            ]
          },
          "from_longitude": {
            "type": "number",
            "description": "Längengrad des Starts.",
            "examples": [
              9.4797
            ]
          },
          "to_country": {
            "type": "string",
            "description": "ISO-Ländercode des Ziels. Siehe `from_country`.",
            "examples": [
              "DE"
            ]
          },
          "to_addr": {
            "type": "string",
            "description": "Klartext-Adresse des Ziels. Siehe `from_addr`.",
            "examples": [
              "Frankfurt am Main, Germany"
            ]
          },
          "to_latitude": {
            "type": "number",
            "examples": [
              50.0379
            ]
          },
          "to_longitude": {
            "type": "number",
            "examples": [
              8.5622
            ]
          },
          "class": {
            "$ref": "#/components/schemas/TransportClass"
          },
          "direction": {
            "type": "string",
            "enum": [
              "import",
              "export"
            ]
          },
          "weight": {
            "type": "number",
            "description": "Gewicht in kg."
          },
          "weight_actual": {
            "type": "number",
            "description": "Ist-Gewicht. Wird zusammen mit `weight_volume` gesendet, setzt der Server\n`weight = max(weight_actual, weight_volume)`.\n"
          },
          "weight_volume": {
            "type": "number",
            "description": "Volumengewicht. Siehe `weight_actual`."
          },
          "etd_range": {
            "type": "string",
            "description": "Abhol-/Abflugfenster als **ein String** mit dem Separator ` to `:\n`\"YYYY-MM-DD HH:MM to YYYY-MM-DD HH:MM\"`. Wird in `etd` / `etd_end` zerlegt.\n\nDer Separator ist exakt Leerzeichen-`to`-Leerzeichen. Bei jedem anderen\nTrennzeichen bleiben **beide** Zielfelder leer, ohne Fehlermeldung.\n",
            "examples": [
              "2026-09-20 08:00 to 2026-09-20 18:00"
            ]
          },
          "eta_range": {
            "type": "string",
            "description": "Ankunftsfenster, gleiches Format wie `etd_range` → `eta` / `eta_end`.",
            "examples": [
              "2026-09-22 08:00 to 2026-09-22 18:00"
            ]
          },
          "etd": {
            "type": "string",
            "description": "Einzelner Abholzeitpunkt statt eines Fensters — Alternative zu `etd_range`.\n\n⚠️ **Die Uhrzeit ist Pflicht.** `\"2026-09-22 14:00:00\"` wird übernommen,\n`\"2026-09-22\"` wird **still verworfen** und das Feld bleibt `null`.\n",
            "examples": [
              "2026-09-20 06:00:00"
            ]
          },
          "eta": {
            "type": "string",
            "description": "Einzelner Ankunftszeitpunkt statt eines Fensters — Alternative zu `eta_range`.\nFür Trucking-Ausschreibungen (`class=rfs`) ist das der Zustelltermin, den auch\ndie Weboberfläche erfragt.\n\n⚠️ **Die Uhrzeit ist Pflicht** — siehe `etd`.\n",
            "examples": [
              "2026-09-22 14:00:00"
            ]
          },
          "category": {
            "type": "string",
            "description": "Ladungsart. Bei `class=rfs` setzt der Server `ftl` als Default.\n",
            "enum": [
              "ftl",
              "ltl"
            ],
            "default": "ftl"
          },
          "dangerous": {
            "$ref": "#/components/schemas/DangerousGoods"
          },
          "pieces": {
            "type": "array",
            "description": "Packstückliste. Bestehende Packstücke der Fracht werden dabei **ersetzt**, nicht\nergänzt — sende beim Bearbeiten immer die vollständige Liste.\n",
            "items": {
              "$ref": "#/components/schemas/Piece"
            }
          },
          "refs": {
            "$ref": "#/components/schemas/FreightRefs"
          },
          "winner_direct": {
            "type": "integer",
            "description": "Firmen-ID des festen Anbieters. Setzt `whitelist = [winner_direct]`,\n`visibility = -2` und `status = open` — die Fracht ist damit sofort scharf und\nnur für diese Firma sichtbar. Ein separates Ausschreiben entfällt.\n"
          },
          "price": {
            "type": "number",
            "description": "Vereinbarter Preis. Sinnvoll nur bei Direktbeauftragung (`type=DTO`), wo Preis\nund Anbieter schon feststehen. Bei einer Ausschreibung entsteht der Preis aus\ndem gewinnenden Angebot.\n"
          },
          "winner": {
            "type": "integer",
            "description": "Firmen-ID des Anbieters bei direkter Anfrage (`type=DTR`) oder\nDirektbeauftragung (`type=DTO`).\n"
          },
          "status": {
            "$ref": "#/components/schemas/FreightStatus"
          },
          "secured": {
            "type": "integer",
            "default": 0,
            "description": "Sichere Lieferkette. Defaultet auf `0`, wenn nicht gesendet."
          },
          "stackable": {
            "type": "integer"
          },
          "dutiable": {
            "type": "integer"
          },
          "awb": {
            "type": "string",
            "description": "Air Waybill."
          },
          "charge": {
            "type": "array",
            "description": "Nur bei `type=FFR` — Charges/Positionen.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "campaign_id": {
            "type": "integer"
          },
          "parent_id": {
            "type": "integer",
            "description": "Übergeordnete Fracht (Tour/LTL-Bündelung)."
          }
        }
      },
      "FreightRefs": {
        "type": "object",
        "title": "refs — Zusatzdaten der Fracht",
        "description": "`refs` ist ein freier JSON-Container auf der Fracht. Er wird unverändert gespeichert\nund beim Abruf zurückgegeben — der richtige Ort für deine eigenen Referenzen.\n\nEinige Schlüssel haben allerdings **Bedeutung für die Plattform**. Die sind unten\naufgeführt; alle anderen sind frei.\n\nDie Weboberfläche schickt beim Anlegen einer Ausschreibung `commodity`, `customer`,\n`pieces`, `weight_actual`, `weight_volume` und `capacity` mit — orientiere dich\ndaran, dann sieht deine Fracht im UI vollständig aus.\n",
        "additionalProperties": true,
        "properties": {
          "customer": {
            "type": "string",
            "description": "Kundenreferenz oder Bestellnummer. Wird im UI als Titel der Fracht angezeigt\n(`display_id`), ist also die sinnvollste Stelle für „woher kommt dieser\nAuftrag\".\n",
            "examples": [
              "Musterkunde GmbH / Bestellung 4711"
            ]
          },
          "commodity": {
            "type": "string",
            "description": "Warenbeschreibung.",
            "examples": [
              "Autoteile"
            ]
          },
          "pieces": {
            "type": "integer",
            "description": "Gesamtzahl Packstücke als Anzeigewert. Ergänzt die Liste in `pieces` —\nersetzt sie nicht.\n",
            "examples": [
              4
            ]
          },
          "weight_actual": {
            "type": "number",
            "description": "Ist-Gewicht in kg (Anzeigewert aus dem Volumenrechner).",
            "examples": [
              480
            ]
          },
          "weight_volume": {
            "type": "number",
            "description": "Frachtpflichtiges Volumengewicht in kg.",
            "examples": [
              640
            ]
          },
          "capacity": {
            "type": "number",
            "description": "Gesamtvolumen in m³.",
            "examples": [
              3.84
            ]
          },
          "incoterm": {
            "type": "string",
            "description": "Lieferbedingung.",
            "enum": [
              "exw",
              "fca",
              "fas",
              "fob",
              "cfr",
              "cif",
              "dat",
              "dap",
              "cpt",
              "cip",
              "ddp"
            ],
            "examples": [
              "dap"
            ]
          },
          "incoterm_place": {
            "type": "string",
            "description": "Benannter Ort zur Lieferbedingung.",
            "examples": [
              "Kassel"
            ]
          },
          "target_rate": {
            "type": "number",
            "description": "Zielpreis, den du erwartest (unverbindliche Angabe an die Anbieter)."
          },
          "target_currency": {
            "type": "string",
            "enum": [
              "EUR",
              "USD",
              "AED"
            ]
          },
          "awb": {
            "type": "string",
            "description": "Air Waybill.",
            "examples": [
              "020-12345675"
            ]
          },
          "uld": {
            "type": "string",
            "description": "ULD-Kennung."
          },
          "entity": {
            "type": "string",
            "description": "Bezug auf ein konkretes Objekt — `rate`, `flight` oder `direct`. Nur zusammen\nmit `obj_id` sinnvoll.\n",
            "enum": [
              "rate",
              "flight",
              "direct"
            ]
          },
          "obj_id": {
            "type": "integer",
            "description": "ID des unter `entity` referenzierten Objekts. Zusammen mit `entity` gesetzt,\nwird die Fracht auf `draft` gestellt und als vom Auftraggeber bestätigt\nmarkiert.\n"
          }
        }
      },
      "Freight": {
        "type": "object",
        "title": "Fracht",
        "description": "Verschachtelte Objekte (`_company`, `_bids`, `_route_pieces`) sind je nach Endpunkt\nund deiner Sichtbarkeit vorhanden oder nicht. Behandle alles mit `_`-Prefix als\noptional.\n\nFelder mit `f_`-Prefix sind **fertig formatierte Anzeigewerte** — direkt verwendbar,\nsiehe unten.\n",
        "properties": {
          "id": {
            "type": "integer"
          },
          "type": {
            "$ref": "#/components/schemas/FreightType"
          },
          "status": {
            "$ref": "#/components/schemas/FreightStatus"
          },
          "class": {
            "$ref": "#/components/schemas/TransportClass"
          },
          "target": {
            "type": "integer",
            "description": "Segment: `2` = Luftfracht, `3` = RFS. Aus `class` abgeleitet."
          },
          "from": {
            "type": "string",
            "description": "Origin, aufgelöster IATA-Code."
          },
          "to": {
            "type": "string"
          },
          "from_country": {
            "type": "string"
          },
          "to_country": {
            "type": "string"
          },
          "from_addr": {
            "type": "string"
          },
          "to_addr": {
            "type": "string"
          },
          "from_latitude": {
            "type": "number"
          },
          "from_longitude": {
            "type": "number"
          },
          "to_latitude": {
            "type": "number"
          },
          "to_longitude": {
            "type": "number"
          },
          "weight": {
            "type": "number"
          },
          "weight_actual": {
            "type": "number"
          },
          "weight_volume": {
            "type": "number"
          },
          "direction": {
            "type": "string"
          },
          "etd": {
            "type": "string",
            "description": "Format `yyyy-MM-dd HH:mm:ss`."
          },
          "etd_end": {
            "type": "string"
          },
          "eta": {
            "type": "string"
          },
          "eta_end": {
            "type": "string"
          },
          "deadline": {
            "type": "string",
            "description": "Ende des Bietfensters."
          },
          "dangerous_goods": {
            "type": "string"
          },
          "secured": {
            "type": "integer"
          },
          "stackable": {
            "type": "integer"
          },
          "dutiable": {
            "type": "integer"
          },
          "awb": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "description": "Verkaufspreis bzw. der Betrag des gewinnenden Angebots. Es gibt **keine**\ngetrennten EK-/VK-Spalten — eingehende Angebote liegen in `bids`, gerechnete\nWerte gehören nach `refs`.\n"
          },
          "visibility": {
            "$ref": "#/components/schemas/Visibility"
          },
          "whitelist": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "company": {
            "type": "integer",
            "description": "Eigentümer-Company (Auftraggeber)."
          },
          "user": {
            "type": "integer"
          },
          "winner": {
            "type": "integer",
            "description": "Company, die den Auftrag erhalten hat."
          },
          "transport_company_id": {
            "type": "integer"
          },
          "delivery_status": {
            "type": "integer"
          },
          "parent_id": {
            "type": "integer"
          },
          "refs": {
            "type": "object",
            "additionalProperties": true
          },
          "created": {
            "type": "string"
          },
          "modified": {
            "type": "string"
          },
          "f_from": {
            "type": "string",
            "description": "Start formatiert als `CODE (LAND)`. Ohne Wert: `???`. Bei maskierten Frachten\n`xxx (XX)`.\n",
            "examples": [
              "FRA (DE)"
            ]
          },
          "f_to": {
            "type": "string",
            "description": "Ziel formatiert, siehe `f_from`.",
            "examples": [
              "MUC (DE)"
            ]
          },
          "f_eta": {
            "type": "string",
            "description": "`eta` formatiert als `YYYY-MM-DD HH:MM` in der Zeitzone der Anwendung, ohne\nSekunden. Ist `eta` leer, steht hier ein übersetzter Platzhalter (deutsch\n`nicht festgelegt`) — **nicht** `null`. Zum Rechnen `eta` verwenden.\n",
            "examples": [
              "2026-09-22 14:00"
            ]
          },
          "f_etd": {
            "type": "string",
            "description": "`etd` formatiert, siehe `f_eta`."
          },
          "f_eta_end": {
            "type": "string",
            "description": "`eta_end` formatiert, siehe `f_eta`."
          },
          "f_etd_end": {
            "type": "string",
            "description": "`etd_end` formatiert, siehe `f_eta`."
          },
          "caption": {
            "type": "string",
            "description": "Einzeiler für Listen und Titel.",
            "examples": [
              "FRA (DE) to MUC (DE) - (Bestellung 4711)"
            ]
          },
          "display_id": {
            "type": "string",
            "description": "Anzeigename der Fracht: die Kundenreferenz aus `refs.customer`, sonst die ID.\n"
          },
          "delivery_status_in_words": {
            "type": "string",
            "description": "Lieferstatus als übersetzter Text.",
            "examples": [
              "unbekannt"
            ]
          },
          "editable": {
            "type": "boolean",
            "description": "Ob du diese Fracht noch bearbeiten darfst."
          },
          "_route_pieces": {
            "type": "array",
            "description": "Die Packstücke der Fracht.",
            "items": {
              "$ref": "#/components/schemas/Piece"
            }
          },
          "_bids": {
            "type": "array",
            "description": "Die Angebote zu dieser Fracht — **der einzige Weg, Angebote abzurufen**.\n\nNur vorhanden, wenn `status` einer von `open`, `pending`, `taken` ist. Gehört\ndie Fracht nicht deiner Firma, enthält das Array ausschließlich deine eigenen\nAngebote.\n",
            "items": {
              "$ref": "#/components/schemas/Quote"
            }
          }
        }
      },
      "Quote": {
        "type": "object",
        "title": "Angebot",
        "description": "Ein Angebot auf eine Fracht, wie es in `Freight._bids` zurückkommt.\n",
        "properties": {
          "id": {
            "type": "integer"
          },
          "route": {
            "type": "integer",
            "description": "ID der Fracht, zu der das Angebot gehört."
          },
          "company": {
            "type": "integer",
            "description": "Firma, die das Angebot abgegeben hat."
          },
          "display_company_id": {
            "type": "integer",
            "description": "Nach außen angezeigte Firma (kann von `company` abweichen)."
          },
          "amount": {
            "type": "number",
            "description": "Gesamtbetrag des Angebots."
          },
          "currency_code": {
            "type": "string",
            "examples": [
              "EUR",
              "USD",
              "AED"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "pending",
              "won",
              "rejected",
              "revoked",
              "expired"
            ],
            "description": "- `open` — abgegeben, noch nicht bestätigt\n- `pending` — von einer Seite bestätigt, wartet auf die andere\n- `won` — Zuschlag erhalten\n- `rejected` / `revoked` / `expired` — nicht mehr im Rennen\n"
          },
          "rate_base": {
            "type": "number"
          },
          "rate_fsc": {
            "type": "number",
            "description": "Treibstoffzuschlag."
          },
          "rate_ssc": {
            "type": "number",
            "description": "Sicherheitszuschlag."
          },
          "rate_all_in": {
            "type": "number"
          },
          "note": {
            "type": "string"
          },
          "flight_number": {
            "type": "string"
          },
          "etd": {
            "type": "string"
          },
          "eta": {
            "type": "string"
          },
          "deadline": {
            "type": "string",
            "description": "Gültigkeit des Angebots."
          },
          "weight": {
            "type": "integer"
          },
          "indicative": {
            "type": "integer",
            "description": "`1` = unverbindliche Indikation statt festes Angebot."
          },
          "carrier_awb": {
            "type": "string"
          },
          "created": {
            "type": "string"
          },
          "modified": {
            "type": "string"
          }
        }
      },
      "QuoteRequest": {
        "type": "object",
        "title": "Angebot",
        "required": [
          "route",
          "amount"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "Bei gesetzter ID wird ein bestehendes Angebot aktualisiert."
          },
          "route": {
            "type": "integer",
            "description": "ID der Fracht, auf die geboten wird."
          },
          "amount": {
            "type": "number",
            "description": "Gesamtbetrag. Wird auf 2 Dezimalstellen gerundet."
          },
          "currency_code": {
            "type": "string",
            "examples": [
              "EUR",
              "USD",
              "AED"
            ]
          },
          "rate_base": {
            "type": "number"
          },
          "rate_fsc": {
            "type": "number",
            "description": "Fuel surcharge."
          },
          "rate_ssc": {
            "type": "number",
            "description": "Security surcharge."
          },
          "rate_all_in": {
            "type": "number"
          },
          "rate_surcharges_base": {
            "type": "number"
          },
          "note": {
            "type": "string"
          },
          "flight_number": {
            "type": "string"
          },
          "etd": {
            "type": "string"
          },
          "eta": {
            "type": "string"
          },
          "deadline": {
            "type": "string",
            "description": "Gültigkeit des Angebots."
          },
          "weight": {
            "type": "integer"
          },
          "indicative": {
            "type": "integer",
            "description": "Unverbindliche Indikation statt festes Angebot."
          },
          "carrierCode": {
            "type": "string",
            "description": "IATA-Code des Carriers; setzt `carrier_awb` aus dem Carrier-Stamm."
          },
          "carrier_representation": {
            "type": "integer",
            "description": "Setzt `display_company_id` auf die Company der Vertretung."
          },
          "display_company_id": {
            "type": "integer",
            "description": "Nach außen angezeigte Company. Default = eigene Company."
          },
          "legs": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "RateSearchRequest": {
        "type": "object",
        "title": "Ratensuche",
        "description": "Kein Feld ist hart validiert; fehlende Werte fallen auf Defaults zurück.\n",
        "properties": {
          "origin": {
            "type": "string",
            "description": "`\"<typ>:<wert>\"` — `airport:FRA` (IATA) oder `city:D-63000` (PLZ).\nDe facto erforderlich.\n",
            "examples": [
              "city:D-63000"
            ]
          },
          "destination": {
            "type": "string",
            "description": "Gleiche Kodierung wie `origin`. De facto erforderlich.",
            "examples": [
              "airport:FRA"
            ]
          },
          "weight": {
            "type": "number",
            "default": 166,
            "description": "Gewicht in kg."
          },
          "search_type": {
            "type": "string",
            "default": "weight",
            "enum": [
              "weight",
              "uld"
            ]
          },
          "uld_count": {
            "type": "integer",
            "description": "Anzahl ULDs, nur bei `search_type=uld`."
          },
          "direction": {
            "type": "string",
            "default": "auto",
            "enum": [
              "auto",
              "import",
              "export"
            ],
            "description": "Bei `auto` wird abgeleitet: city → airport = Export, airport → city = Import.\n"
          },
          "allow_combinations": {
            "description": "Truthy (Default). `0`, `false` oder `''` deaktiviert Multi-Leg-Kombinationen.\n",
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string"
              },
              {
                "type": "boolean"
              }
            ]
          },
          "sheet_id": {
            "type": "integer",
            "description": "Suche auf ein einzelnes Rate-Sheet einschränken."
          },
          "pieces_json": {
            "type": "array",
            "description": "Packstückliste. Längeneinheit über `unitOfLength`. Leitet Ist- und\nVolumengewicht ab.\n",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/Piece"
                },
                {
                  "type": "object",
                  "properties": {
                    "weight_per_piece": {
                      "type": "number"
                    }
                  }
                }
              ]
            }
          },
          "unitOfLength": {
            "type": "string",
            "default": "cm",
            "enum": [
              "cm",
              "inch"
            ]
          },
          "calc_payload": {
            "type": "string",
            "description": "JSON-String aus dem Volumenrechner des UI."
          }
        }
      },
      "RateSearchResult": {
        "type": "object",
        "title": "Suchergebnis",
        "properties": {
          "rates": {
            "type": "array",
            "description": "Direkte Raten, max. 200.",
            "items": {
              "$ref": "#/components/schemas/Rate"
            }
          },
          "combinations": {
            "type": "object",
            "description": "Gefundene Multi-Leg-Kombinationen.",
            "properties": {
              "results": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": true
                }
              },
              "debug": {
                "type": "object",
                "additionalProperties": true
              }
            }
          },
          "weight": {
            "type": "number"
          },
          "search_type": {
            "type": "string"
          },
          "uld_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "selected_direction": {
            "type": "string",
            "description": "Was angefragt wurde."
          },
          "effective_direction": {
            "type": "string",
            "description": "Was daraus abgeleitet wurde."
          },
          "weight_actual": {
            "type": [
              "number",
              "null"
            ]
          },
          "weight_volumen166": {
            "type": [
              "number",
              "null"
            ]
          },
          "searchEntity": {
            "type": "object",
            "description": "Die persistierte Suche. `searchEntity.id` ist als `pricing_search_id` beim\nAnlegen einer Fracht wiederverwendbar.\n",
            "properties": {
              "id": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Rate": {
        "type": "object",
        "title": "Rate",
        "description": "Die Feldpräsenz variiert je nach Rate-Typ. Verlässlich gesetzt sind\n`price_total`, `charges`, `calc_details` und `_fuel_surcharge` — auf diese vier\nkannst du dich stützen.\n",
        "properties": {
          "id": {
            "type": "integer"
          },
          "price_total": {
            "type": "number"
          },
          "charges": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "calc_details": {
            "type": "object",
            "additionalProperties": true
          },
          "_fuel_surcharge": {
            "type": "object",
            "additionalProperties": true
          },
          "sheet": {
            "type": "object",
            "properties": {
              "_is_own": {
                "type": "boolean",
                "description": "Gehört das Rate-Sheet der eigenen Company?"
              },
              "company": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "name": {
                    "type": "string"
                  },
                  "logo_full": {
                    "type": "string"
                  }
                }
              },
              "associated_company": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      }
    }
  }
}