MENU navbar-image

Introduction

REST-Schnittstelle für Landingpages zur Angebots- und Buchungsanfrage von Fahrten bei Primos.

Diese Schnittstelle erlaubt es Landingpages (LPs), bei Primos ein Angebot für eine Fahrt anzufragen
(verfügbare Fahrzeuge, Distanz, Preise) und anschließend eine Buchung anzulegen. Eine Buchung erzeugt
im Primos-CRM eine Fahrt (Status „Anfrage") und löst dort die üblichen Automatismen aus.

**Typischer Ablauf:**

1. `POST /api/v1/quotes` – Angebot anfragen. Antwort enthält `quote_id` sowie ein einmalig sichtbares `quote_token`.
2. `POST /api/v1/bookings` – mit `quote_id`, `quote_token` und gewähltem `vehicle_id` buchen.

Preise sind im Angebot eingefroren; die Buchung verwendet ausschließlich den serverseitig berechneten Preis.
Ein Angebot ist zeitlich begrenzt gültig und kann nur einmal gebucht werden.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Jeder Zugang gehört zu einer Auftragsquelle. Das Bearer-Token wird von Primos im Admin-Bereich erstellt und verwaltet. Token besitzen Berechtigungen: quote (Angebote) und book (Buchungen).

Origin-Beschränkung (optional): Für eine Auftragsquelle kann eine Liste erlaubter Origins hinterlegt sein. Ist sie gesetzt, werden Browser-Anfragen nur von einer dieser Origins akzeptiert (sonst 403); Anfragen ohne Origin-Header (Server-zu-Server) sind nicht betroffen. Ist keine Liste hinterlegt, gilt keine Einschränkung. Bei Bedarf teilt Primos die freigeschalteten Origins mit.

Angebote

Angebot anfragen

requires authentication

Berechnet für die angegebene Strecke die verfügbaren Fahrzeuge samt Distanz und Preisen und legt ein zeitlich begrenztes Angebot an.

Locator-Regeln (identisch zum Website-Rechner): Jede Seite (pickup/destination) ist entweder eine Adresse oder ein Flughafen.

Bei type=airport muss genau eine Seite ein airport_iata enthalten (die andere ist eine Adress-Seite); bei type=transfer sind beide Seiten Adress-Seiten und airport_iata ist nicht erlaubt.

Example request:
curl --request POST \
    "https://primos-fahrservice.de/api/v1/quotes" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"airport\",
    \"pickup\": {
        \"airport_iata\": \"fra\",
        \"place_id\": \"ChIJ7cv00DwsDogRAMDACa2m4K8\",
        \"formatted_address\": \"Bahnhofstraße 1, 65552 Limburg an der Lahn, Deutschland\",
        \"street\": \"Bahnhofstraße\",
        \"house_number\": \"1\",
        \"postal_code\": \"65552\",
        \"city\": \"Limburg an der Lahn\"
    },
    \"destination\": {
        \"airport_iata\": \"fra\",
        \"place_id\": \"ChIJeflCVHQLvUcRMfP4IU3YdIo\",
        \"formatted_address\": \"Zeil 1, 60313 Frankfurt am Main, Deutschland\",
        \"street\": \"Zeil\",
        \"house_number\": \"1\",
        \"postal_code\": \"60313\",
        \"city\": \"Frankfurt am Main\"
    },
    \"pickup_time\": \"2026-07-10T14:30:00+02:00\",
    \"roundtrip\": false,
    \"passengers\": 1,
    \"child_seats\": 0,
    \"flight_number\": \"LH123\",
    \"options\": {
        \"sign_service\": false
    }
}"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/quotes"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "airport",
    "pickup": {
        "airport_iata": "fra",
        "place_id": "ChIJ7cv00DwsDogRAMDACa2m4K8",
        "formatted_address": "Bahnhofstraße 1, 65552 Limburg an der Lahn, Deutschland",
        "street": "Bahnhofstraße",
        "house_number": "1",
        "postal_code": "65552",
        "city": "Limburg an der Lahn"
    },
    "destination": {
        "airport_iata": "fra",
        "place_id": "ChIJeflCVHQLvUcRMfP4IU3YdIo",
        "formatted_address": "Zeil 1, 60313 Frankfurt am Main, Deutschland",
        "street": "Zeil",
        "house_number": "1",
        "postal_code": "60313",
        "city": "Frankfurt am Main"
    },
    "pickup_time": "2026-07-10T14:30:00+02:00",
    "roundtrip": false,
    "passengers": 1,
    "child_seats": 0,
    "flight_number": "LH123",
    "options": {
        "sign_service": false
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "data": {
        "quote_id": "9b1f2c34-1a2b-4c3d-8e9f-000000000000",
        "type": "transfer",
        "distance_km": 42,
        "roundtrip": true,
        "currency": "EUR",
        "expires_at": "2026-07-15T14:30:00+02:00",
        "vehicles": [
            {
                "vehicle_id": 3,
                "name": "Business Van",
                "passengers": 7,
                "luggage": 7,
                "child_seats": 3,
                "image": "vehicles/van.jpg",
                "price": {
                    "amount": 189,
                    "currency": "EUR",
                    "roundtrip_included": true
                }
            }
        ]
    },
    "meta": {
        "quote_token": "einmalig-sichtbarer-token"
    }
}
 

Request   

POST api/v1/quotes

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

type   string     

Fahrttyp. Erlaubt: transfer (Adresse → Adresse) oder airport (Adresse ↔ Flughafen). Example: airport

Must be one of:
  • transfer
  • airport
pickup   object     

Abholort. Entweder eine Adresse (place_id + formatted_address + street + house_number) oder – nur bei airport – ein Flughafen (airport_iata).

airport_iata   string  optional    

IATA-Code aus GET /api/v1/airports, wenn der Abholort ein Flughafen ist. Nur bei type=airport und genau auf einer der beiden Seiten. Wert unverändert übernehmen (case-sensitive). Must match an existing stored value. Example: fra

place_id   string  optional    

Google-Place-ID des Abholorts, unverändert aus Google Places Autocomplete. Pflicht für Adress-Seiten. Nicht angeben, wenn die Seite ein Flughafen ist. This field is required when pickup.airport_iata is not present. Example: ChIJ7cv00DwsDogRAMDACa2m4K8

formatted_address   string  optional    

Von Google gelieferte, vollständige Adresse (Anzeige & CRM). Pflicht für Adress-Seiten. This field is required when pickup.airport_iata is not present. Must not be greater than 500 characters. Example: Bahnhofstraße 1, 65552 Limburg an der Lahn, Deutschland

street   string  optional    

Straße (aus den Place-Details). Pflicht für Adress-Seiten. This field is required when pickup.airport_iata is not present. Must not be greater than 255 characters. Example: Bahnhofstraße

house_number   string  optional    

Hausnummer (aus den Place-Details). Pflicht für Adress-Seiten. This field is required when pickup.airport_iata is not present. Must not be greater than 50 characters. Example: 1

postal_code   string  optional    

Optionale Postleitzahl. Must not be greater than 20 characters. Example: 65552

city   string  optional    

Optionaler Ort. Must not be greater than 255 characters. Example: Limburg an der Lahn

destination   object     

Zielort. Gleiche Regeln wie pickup: Adresse (place_id + formatted_address + street + house_number) oder – nur bei airport – ein Flughafen (airport_iata).

airport_iata   string  optional    

IATA-Code aus GET /api/v1/airports, wenn das Ziel ein Flughafen ist. Nur bei type=airport und genau auf einer der beiden Seiten. Must match an existing stored value. Example: fra

place_id   string  optional    

Google-Place-ID des Ziels aus Google Places Autocomplete. Pflicht für Adress-Seiten. Nicht angeben, wenn die Seite ein Flughafen ist. This field is required when destination.airport_iata is not present. Example: ChIJeflCVHQLvUcRMfP4IU3YdIo

formatted_address   string  optional    

Von Google gelieferte, vollständige Adresse. Pflicht für Adress-Seiten. This field is required when destination.airport_iata is not present. Must not be greater than 500 characters. Example: Zeil 1, 60313 Frankfurt am Main, Deutschland

street   string  optional    

Straße (aus den Place-Details). Pflicht für Adress-Seiten. This field is required when destination.airport_iata is not present. Must not be greater than 255 characters. Example: Zeil

house_number   string  optional    

Hausnummer (aus den Place-Details). Pflicht für Adress-Seiten. This field is required when destination.airport_iata is not present. Must not be greater than 50 characters. Example: 1

postal_code   string  optional    

Optionale Postleitzahl. Must not be greater than 20 characters. Example: 60313

city   string  optional    

Optionaler Ort. Must not be greater than 255 characters. Example: Frankfurt am Main

pickup_time   string     

Abholzeitpunkt (ISO 8601), muss in der Zukunft liegen. Must be a valid date. Must be a date after now. Example: 2026-07-10T14:30:00+02:00

roundtrip   boolean  optional    

true für Hin- und Rückfahrt. Example: false

return_time   string  optional    

Rückfahrt-Zeitpunkt (ISO 8601), Pflicht bei roundtrip=true, muss nach pickup_time liegen. This field is required when roundtrip is true. Must be a valid date. Must be a date after pickup_time.

passengers   integer     

Anzahl Passagiere (1–99). Must be at least 1. Must not be greater than 99. Example: 1

child_seats   integer  optional    

Anzahl Kindersitze (0–99). Must be at least 0. Must not be greater than 99. Example: 0

flight_number   string  optional    

Optionale Flugnummer. Must not be greater than 20 characters. Example: LH123

options   object  optional    
sign_service   boolean  optional    

true bucht den Schilderservice (Abholung mit Namensschild). Example: false

Angebot abrufen

requires authentication

Liefert ein zuvor erstelltes Angebot erneut (ohne das einmalige Token).

Example request:
curl --request GET \
    --get "https://primos-fahrservice.de/api/v1/quotes/019f435c-eb4e-732a-8a53-03b2e1a7f6f6" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/quotes/019f435c-eb4e-732a-8a53-03b2e1a7f6f6"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "quote_id": "9b1f2c34-1a2b-4c3d-8e9f-000000000000",
        "type": "transfer",
        "distance_km": 42,
        "roundtrip": true,
        "currency": "EUR",
        "expires_at": "2026-07-15T14:30:00+02:00",
        "vehicles": [
            {
                "vehicle_id": 3,
                "name": "Business Van",
                "passengers": 7,
                "luggage": 7,
                "child_seats": 3,
                "image": "vehicles/van.jpg",
                "price": {
                    "amount": 189,
                    "currency": "EUR",
                    "roundtrip_included": true
                }
            }
        ]
    }
}
 

Example response (404):


{
    "message": "No query results for model [App\\Models\\Quote]."
}
 

Request   

GET api/v1/quotes/{id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

id   string     

The ID of the quote. Example: 019f435c-eb4e-732a-8a53-03b2e1a7f6f6

quote   string     

Die quote_id des Angebots. Example: 9b1f2c34-1a2b-4c3d-8e9f-000000000000

Authentifizierung

Eigene Auftragsquelle

requires authentication

Gibt die zum Token gehörende Auftragsquelle samt Berechtigungen zurück – nützlich als Token-Check.

Example request:
curl --request GET \
    --get "https://primos-fahrservice.de/api/v1/me" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/me"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "name": "LP Flughafentransfer FFM",
        "slug": "lp-ffm",
        "contact_email": "[email protected]",
        "is_active": true,
        "abilities": [
            "quote",
            "book"
        ]
    }
}
 

Request   

GET api/v1/me

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Buchungen

Fahrt buchen

requires authentication

Bucht ein zuvor erstelltes Angebot und legt im CRM eine Fahrt an (Status „Anfrage"), wodurch die Automatismen (Benachrichtigungen, Kalender) ausgelöst werden. Der Preis wird immer aus dem Angebot übernommen – ein im Request gesendeter Preis wird ignoriert.

Optionaler Header Idempotency-Key: Wird derselbe Schlüssel erneut gesendet, wird die bereits erstellte Buchung zurückgegeben (Status 200 statt 201), ohne eine zweite Fahrt anzulegen.

Example request:
curl --request POST \
    "https://primos-fahrservice.de/api/v1/bookings" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Idempotency-Key: abc-123" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"quote_id\": \"6ff8f7f6-1eb3-3525-be4a-3932c805afed\",
    \"quote_token\": \"architecto\",
    \"vehicle_id\": 16,
    \"customer\": {
        \"firstname\": \"b\",
        \"lastname\": \"n\",
        \"email\": \"[email protected]\",
        \"phone\": \"v\"
    },
    \"payment_method\": \"paypal\",
    \"billing\": {
        \"different\": true,
        \"name\": \"d\",
        \"street\": \"l\",
        \"house_number\": \"j\",
        \"postal_code\": \"nikhwaykcmyuwpwl\",
        \"city\": \"v\"
    },
    \"notes\": \"q\",
    \"reference\": \"w\"
}"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/bookings"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Idempotency-Key": "abc-123",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "quote_id": "6ff8f7f6-1eb3-3525-be4a-3932c805afed",
    "quote_token": "architecto",
    "vehicle_id": 16,
    "customer": {
        "firstname": "b",
        "lastname": "n",
        "email": "[email protected]",
        "phone": "v"
    },
    "payment_method": "paypal",
    "billing": {
        "different": true,
        "name": "d",
        "street": "l",
        "house_number": "j",
        "postal_code": "nikhwaykcmyuwpwl",
        "city": "v"
    },
    "notes": "q",
    "reference": "w"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "data": {
        "booking_id": 12345,
        "status": "inquiry",
        "type": "transfer",
        "vehicle": {
            "vehicle_id": 3,
            "name": "Business Van"
        },
        "pickup_time": "2026-07-15T14:30:00+02:00",
        "roundtrip": true,
        "external_reference": "LP-ORDER-5567",
        "price": {
            "amount": 189,
            "currency": "EUR"
        },
        "created_at": "2026-07-08T12:00:00+02:00"
    }
}
 

Example response (409):


{
    "message": "Angebot ist abgelaufen oder wurde bereits gebucht."
}
 

Request   

POST api/v1/bookings

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Idempotency-Key        

Example: abc-123

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

quote_id   string     

Must be a valid UUID. Must match an existing stored value. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

quote_token   string     

Example: architecto

vehicle_id   integer     

Example: 16

customer   object     
firstname   string     

Must not be greater than 255 characters. Example: b

lastname   string     

Must not be greater than 255 characters. Example: n

email   string     

Must be a valid email address. Must not be greater than 255 characters. Example: [email protected]

phone   string     

Must not be greater than 50 characters. Example: v

payment_method   string  optional    

Example: paypal

Must be one of:
  • bar
  • paypal
  • ec
  • invoice
billing   object  optional    
different   boolean  optional    

Example: true

name   string  optional    

Must not be greater than 255 characters. Example: d

street   string  optional    

Must not be greater than 255 characters. Example: l

house_number   string  optional    

Must not be greater than 50 characters. Example: j

postal_code   string  optional    

Must not be greater than 20 characters. Example: nikhwaykcmyuwpwl

city   string  optional    

Must not be greater than 255 characters. Example: v

notes   string  optional    

Must not be greater than 2000 characters. Example: q

reference   string  optional    

Must not be greater than 255 characters. Example: w

Buchungsstatus abrufen

requires authentication

Liefert den aktuellen Status einer Buchung (z. B. inquiry, confirmed, booked, done).

Example request:
curl --request GET \
    --get "https://primos-fahrservice.de/api/v1/bookings/40" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/bookings/40"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "booking_id": 12345,
        "status": "confirmed",
        "type": "transfer",
        "vehicle": {
            "vehicle_id": 3,
            "name": "Business Van"
        },
        "pickup_time": "2026-07-15T14:30:00+02:00",
        "roundtrip": true,
        "external_reference": "LP-ORDER-5567",
        "price": {
            "amount": 189,
            "currency": "EUR"
        },
        "created_at": "2026-07-08T12:00:00+02:00"
    }
}
 

Request   

GET api/v1/bookings/{drive_id}

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

drive_id   integer     

The ID of the drive. Example: 40

drive   integer     

Die booking_id (Fahrt-ID). Example: 12345

Fahrzeuge

Fahrzeugkatalog

requires authentication

Liefert die aktuell angebotenen Fahrzeuge (ohne Preise – Preise entstehen erst im Angebot).

Example request:
curl --request GET \
    --get "https://primos-fahrservice.de/api/v1/vehicles" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/vehicles"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "vehicle_id": 3,
            "name": "Business Van",
            "description": "Bis zu 7 Personen",
            "passengers": 7,
            "luggage": 7,
            "child_seats": 3,
            "image": "vehicles/van.jpg"
        },
        {
            "vehicle_id": 1,
            "name": "Business Limousine",
            "description": "Mercedes E-Klasse o. ä.",
            "passengers": 3,
            "luggage": 3,
            "child_seats": 1,
            "image": "vehicles/limo.jpg"
        }
    ]
}
 

Request   

GET api/v1/vehicles

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Flughäfen

Flughäfen

requires authentication

Liefert die aktuell bedienten Flughäfen. Der zurückgegebene iata-Wert ist exakt der Wert, der bei type=airport-Angeboten als pickup.airport_iata bzw. destination.airport_iata gesendet werden muss. Bitte diese Liste als einzige Quelle für gültige IATA-Codes nutzen und die Werte unverändert übernehmen (der Vergleich erfolgt case-sensitive) – so entstehen keine Abweichungen zwischen Landingpage und Angebotsanfrage.

Die mitgelieferten Koordinaten (lat/lng) und die Google-place_id entsprechen dem, was Primos serverseitig für die Distanzberechnung verwendet, und können für ein eigenes Google-Maps-Autocomplete genutzt werden.

Example request:
curl --request GET \
    --get "https://primos-fahrservice.de/api/v1/airports" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/airports"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "iata": "fra",
            "name": "Frankfurt International Airport",
            "lat": 50.0353788,
            "lng": 8.5518391,
            "place_id": "ChIJeflCVHQLvUcRMfP4IU3YdIo"
        }
    ]
}
 

Request   

GET api/v1/airports

Headers

Authorization        

Example: Bearer {YOUR_AUTH_KEY}

Content-Type        

Example: application/json

Accept        

Example: application/json

Status

Public liveness endpoint.

Example request:
curl --request GET \
    --get "https://primos-fahrservice.de/api/v1/health" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://primos-fahrservice.de/api/v1/health"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):

Show headers
cache-control: no-cache, private
content-type: application/json
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
access-control-allow-origin: *
 

{
    "status": "ok",
    "api": "primos-lp",
    "version": "v1"
}
 

Request   

GET api/v1/health

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json