Pricing & Estimates

import \{ Callout } from 'nextra/components'

Pre-Order Information & Estimates

Before creating an order, you must use the aggregated preorder endpoint to gather all essential pre-order information. This single endpoint provides everything you need to present booking options to your customers and make informed decisions.

Get Aggregated Pre-Order Information

The aggregated preorder endpoint is your one-stop solution for retrieving comprehensive pre-order data, including:

  1. Service Availability - Which services are currently available for your route
  2. Service Capabilities - Passenger and luggage capacity for each service tier
  3. Estimated Prices - Real-time pricing with currency formatting
  4. Expected Arrival Time - ETA ranges for driver pickup

This endpoint provides all the information needed to present complete booking options to your users.

Endpoint

GET https://api.gett.com/v1/private/preorder/aggregated

Headers

Content-Type: application/json
Authorization: Bearer {your_token}

Query Parameters

ParameterTypeRequiredDescription
partner_idstringYesYour partner ID provided by Gett

Request Body

ParameterTypeRequiredDescription
stopsarrayYesArray of stop objects defining the route
stops[].typestringYesType of stop: origin, destination, or on_going
stops[].locationobjectYesLocation coordinates
stops[].location.latfloatYesLatitude coordinate
stops[].location.lngfloatYesLongitude coordinate
scheduled_atstringNoPickup time in ISO8601 format (e.g., 2025-09-28T13:35:00Z)
categorystringYesService category: transportation or delivery
localestringYesLanguage code (e.g., en, es, fr)
payment_typestringYesPayment method: cash (only option for partners)

Example Request

curl --location 'https://api.gett.com/v1/private/preorder/aggregated?partner_id=1f4c281e-b3eb-4676-beb7-b3a61caba0e6' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer {token}' \
  --data '{
    "stops": [
      {
        "type": "origin",
        "location": {
          "lat": 51.473401, 
          "lng": -0.0221145
        }
      },
      {
        "type": "destination",
        "location": {
          "lat": 51.4712475, 
          "lng": 0.0357987
        }
      }
    ],
    "category": "transportation",
    "locale": "en",
    "payment_type": "cash"
  }'

Example Response

{
  "routes": [
    {
      "uuid": "c41116f6-697c-4799-80e6-af0953a61b00",
      "provider": "google",
      "polyline": "mldyHdgCa@PK?_@Mw@iAG?U[Da@HUN_@Xs@p@yBf@eBj@iCXiBRwCJmBDk@@i@?aBA_ACaAB{@H_ANSh@eBb@qAPw@Fm@F{@?yAIaCWeFe@uHEoBFeCLcBPmCAM@_@D_@D{@VgDd@wGN}@^{FPmCDkBCwBMmE_Cmb@k@mKIqAG_@YmAO[IUASBUFMHi@J_AC]u@mMe@iIKoBEKCGSoCGw@@m@@OIcAa@_IWcFIoAK}AKYK_AKkAg@eFi@cGO{@EGKCWUKWIi@?a@Fg@LY@k@@q@SgBWmDYqEI{EAsDHmBTEtB]jAORFJNP^DNbBoAdBiAtBwAnA{@jBiAz@_@`Ac@EYm@qE]gDEq@O_E",
      "scheduled_at": "2025-10-06T10:34:43Z",
      "distance": 4624,
      "duration": 703,
      "duration_unit": "s",
      "distance_unit": "m",
      "selected_route": true,
      "label": ["manual"]
    }
  ],
  "product_with_prices": [
    {
      "product": {
        "product_id": "74352801-2d3d-4235-966e-11e07b0e076a",
        "name": "Guest Booking",
        "icon": "https://static.gett.com/services/media/media_files/28207/uk_black-cab__1.png",
        "description": "Iconic, spacious black cabs with 5 or 6 seats that can cut through traffic using the bus lanes to get you to your destination faster",
        "eta_sec": 206,
        "eta_description": "Under 4 min",
        "booking_settings": {
          "on_demand": {
            "allowed": true
          },
          "pre_book": {
            "allowed": true,
            "pre_book_in_advance_min": 30,
            "edit_enabled": true,
            "edit_before_scheduled_at_min": 30,
            "destination_mandatory": false
          },
          "stops": {
            "mandatory_origin": true,
            "mandatory_destination": false,
            "ongoing_stops": {
              "allowed": true,
              "max_ongoing_stops": 3
            }
          },
          "multi_riders": {
            "allowed": true,
            "max_tracked_riders": 5
          },
          "max_available_seats": 5,
          "note_to_driver": {
            "allowed": true,
            "mandatory": false
          }
        }
      },
      "price": {
        "pricing_options": [
          {
            "estimation_id": "6f1caae3-d3fa-4b18-992f-ae0a374acfab",
            "price_concept": "meter_range",
            "calculated_as": "meter",
            "include_vat": true,
            "currency": "£",
            "currency_iso": "GBP",
            "tech_fee": {
              "applicable": false,
              "value": 0
            },
            "additional_fee": [
              {
                "name": "driving",
                "value": "18.6",
                "min": 17,
                "max": 20
              }
            ],
            "properties": [
              {
                "name": "meter_total",
                "min": 17,
                "max": 20,
                "is_range": true,
                "amount": "18.6",
                "percentage": null
              }
            ]
          }
        ]
      },
      "eta_to_destination": "0001-01-01T00:00:00Z"
    }
  ]
}

Understanding the Pre-Order Response

The aggregated response provides four critical pieces of information through two main sections:

Routes Section

Contains route and journey information:

  • Route Details: Distance (in meters), duration (in seconds), and route polyline
  • Scheduling: Confirmation of scheduled pickup time
  • Provider: Route calculation provider (e.g., Google)

Product with Prices Section

Contains available services and their details:

1. Service Availability

Each product includes booking settings that indicate availability:

  • On Demand: Whether immediate booking is allowed
  • Pre-book: Whether advance booking is allowed with minimum advance time
  • Stops Configuration: Maximum ongoing stops and destination requirements

2. Service Capabilities

Detailed service capabilities in booking_settings:

  • max_available_seats: Maximum passenger capacity
  • max_ongoing_stops: Maximum intermediate stops allowed
  • max_tracked_riders: Maximum riders that can be tracked
  • note_to_driver: Whether driver notes are supported

3. Estimated Prices

Pricing information in the price.pricing_options array:

  • estimation_id: Unique quote identifier used as quote_id when creating orders
  • price_concept: Pricing model - meter, meter_range, or fixed_price
    • meter: Final price calculated by taxi meter (estimate may be 0 or minimal)
    • meter_range: Final price calculated by meter, with estimated range provided (min/max values)
    • fixed_price: Price is committed when you create the order using this estimation_id as quote_id
  • calculated_as: Method used to calculate the price (e.g., "meter", "distance")
  • currency & currency_iso: Currency symbol and ISO code
  • include_vat: Whether VAT is included in pricing
  • tech_fee: Technology fee details and applicability
  • additional_fee: Additional fees with min/max ranges (for meter_range pricing)
  • properties: Pricing breakdown with amounts and ranges
    • is_range: Indicates if the price includes min/max estimates
    • min & max: Price range boundaries (for meter_range)
    • amount: Estimated or fixed price amount

Price Commitment: When price_concept is fixed_price, providing the estimation_id as quote_id in your order creation request commits the displayed price. For meter or meter_range pricing, the quote_id is still required but the final price will be determined by the taxi meter.

4. Expected Arrival Time (ETA)

Driver pickup time estimates:

  • eta_sec: ETA in seconds from booking
  • eta_description: Human-readable ETA description
  • eta_to_destination: Estimated arrival time at destination

Service Details

Additional service information:

  • product_id: Unique identifier for booking
  • name & description: Service display information
  • icon: Service icon URL for UI display

Always call the aggregated endpoint before creating orders. Prices, availability, and ETAs can change rapidly. Never attempt to create an order without first getting current pre-order information.

Pricing Types

The aggregated response includes pricing information with different pricing models:

Meter Pricing

When price_concept is "meter" or "meter_range", the price shown is an estimate only:

Simple Meter Pricing:

{
  "estimation_id": "4f3cc8ba-b24e-42d9-aef8-3ce528f603fd",
  "price_concept": "meter",
  "calculated_as": "meter",
  "properties": [
    {
      "name": "meter_total",
      "amount": "0",
      "is_range": false
    }
  ]
}

Meter Range Pricing (with estimated range):

{
  "estimation_id": "6f1caae3-d3fa-4b18-992f-ae0a374acfab",
  "price_concept": "meter_range",
  "calculated_as": "meter",
  "additional_fee": [
    {
      "name": "driving",
      "value": "18.6",
      "min": 17,
      "max": 20
    }
  ],
  "properties": [
    {
      "name": "meter_total",
      "min": 17,
      "max": 20,
      "is_range": true,
      "amount": "18.6"
    }
  ]
}
  • The estimation_id must be provided as quote_id when creating the order
  • The final price will be calculated by the taxi meter during the ride
  • For meter_range, the displayed range (min/max) and estimated amount are for reference only
  • The actual fare may differ from the estimate

Fixed Price

When price_concept is "fixed_price", the price is committed when you create the order:

{
  "estimation_id": "1a22584a-9685-4153-a629-271ebc34dcda",
  "price_concept": "fixed_price",
  "calculated_as": "distance",
  "properties": [
    {
      "name": "fixed_total",
      "amount": "25.50",
      "is_range": false
    }
  ]
}
  • Providing the estimation_id as quote_id locks in the displayed price
  • The customer pays exactly the amount shown, regardless of route changes
  • Price is guaranteed at order creation time

Fixed Price Advantage: When fixed_price is available, using the quote_id guarantees the exact fare to your customer, providing price certainty and transparency.

Integration Workflow

Required Pre-Order Flow

  1. User selects route → Call aggregated endpoint
  2. Display options → Show available services with prices, capacity, and ETAs
  3. User selects service → Validate selection is still available
  4. Create order → Use the product_id and estimation_id (as quote_id) from the aggregated response

When creating an order, you must provide the quote_id parameter with the value of estimation_id from the selected product's pricing_options array. For services with price_concept: "fixed_price", this commits the displayed price. For price_concept: "meter", the quote is required but the final price is determined by the taxi meter.

Best Practices

Real-time Updates

  • Cache pre-order data for maximum 2-3 minutes to balance performance and accuracy
  • Refresh automatically when route parameters change
  • Show users when information was last updated
  • Re-fetch before order creation if data is older than 2 minutes

Handling Availability Changes

async function getPreOrderInfo(routeData) {
  try {
    const response = await fetch(`/v1/private/preorder/aggregated?partner_id=${partnerId}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(routeData)
    });

    if (!response.ok) {
      if (response.status === 400) {
        throw new Error('Invalid route parameters');
      } else if (response.status === 404) {
        throw new Error('No services available for this route');
      }
      throw new Error('Failed to get pre-order information');
    }

    const data = await response.json();
    
    // Check if we have products and routes
    if (!data.product_with_prices || data.product_with_prices.length === 0) {
      throw new Error('No services currently available for this route');
    }

    if (!data.routes || data.routes.length === 0) {
      throw new Error('No valid routes found for this request');
    }

    // Filter products that allow the requested booking type
    const availableServices = data.product_with_prices.filter(item => {
      const isOnDemand = !routeData.scheduled_at;
      if (isOnDemand) {
        return item.product.booking_settings.on_demand?.allowed;
      } else {
        return item.product.booking_settings.pre_book?.allowed;
      }
    });
    
    if (availableServices.length === 0) {
      throw new Error('No services available for the requested booking type');
    }

    return {
      ...data,
      product_with_prices: availableServices
    };
  } catch (error) {
    console.error('Pre-order info request failed:', error);
    throw error;
  }
}

Multiple Stops

For routes with multiple stops, include all stops in the request:

{
  "stops": [
    {
      "type": "origin",
      "location": {
        "lat": 51.4988787,
        "lng": -0.020746
      }
    },
    {
      "type": "on_going",
      "location": {
        "lat": 51.4899,
        "lng": -0.0125
      }
    },
    {
      "type": "destination",
      "location": {
        "lat": 51.4712475,
        "lng": 0.0357987
      }
    }
  ]
}

Essential Integration Requirements

Mandatory Pre-Order Check: You must call the aggregated endpoint for every booking attempt. This is not optional - it's required to ensure service availability and get current pricing.

Critical Implementation Points

  1. Always Check All Four Aspects:

    • ✅ Service availability before showing options
    • ✅ Service capabilities to match user needs
    • ✅ Current prices for accurate billing
    • ✅ Real ETAs for user expectations
  2. User Experience Guidelines:

    • Display all available service tiers with clear capability differences
    • Show real-time ETAs to set proper expectations
    • Update information when route changes
    • Handle "no services available" scenarios gracefully
  3. Technical Requirements:

    • Never cache pre-order data longer than 2-3 minutes
    • Always validate selected service is still available before order creation
    • Implement proper loading states during API calls
    • Handle network failures and API errors appropriately

Complete Integration Example

class BookingFlow {
  async displayBookingOptions(origin, destination, scheduledTime) {
    // Step 1: Get all pre-order information
    const preOrderData = await this.getPreOrderInfo({
      stops: [
        { type: 'origin', location: origin },
        { type: 'destination', location: destination }
      ],
      scheduled_at: scheduledTime,
      category: 'transportation',
      locale: 'en',
      payment_type: 'cash'
    });

    // Step 2: Extract route information
    const selectedRoute = preOrderData.routes.find(route => route.selected_route) || preOrderData.routes[0];
    const routeInfo = {
      distance: `${(selectedRoute.distance / 1000).toFixed(1)} km`,
      duration: `${Math.round(selectedRoute.duration / 60)} min`,
      scheduledAt: selectedRoute.scheduled_at
    };

    // Step 3: Process available services
    const availableServices = preOrderData.product_with_prices.filter(item => {
      const isOnDemand = !scheduledTime;
      return isOnDemand 
        ? item.product.booking_settings.on_demand?.allowed
        : item.product.booking_settings.pre_book?.allowed;
    });

    if (availableServices.length === 0) {
      throw new Error('No services available for this route at this time');
    }

    // Step 4: Format service options for display
    return availableServices.map(item => {
      const product = item.product;
      const pricing = item.price.pricing_options[0];
      
      return {
        id: product.product_id,
        name: product.name,
        description: product.description,
        icon: product.icon,
        eta: product.eta_description,
        etaSeconds: product.eta_sec,
        maxSeats: product.booking_settings.max_available_seats,
        maxOngoingStops: product.booking_settings.stops.ongoing_stops.max_ongoing_stops,
        pricing: {
          concept: pricing.price_concept,
          currency: pricing.currency,
          currencyIso: pricing.currency_iso,
          includeVat: pricing.include_vat,
          techFee: pricing.tech_fee
        },
        bookingSettings: product.booking_settings,
        routeInfo: routeInfo
      };
    });
  }

  async createOrderWithValidation(selectedProductId, orderData) {
    // Re-validate availability before creating order
    const currentInfo = await this.getPreOrderInfo(orderData.routeInfo);
    const selectedProduct = currentInfo.product_with_prices.find(
      item => item.product.product_id === selectedProductId
    );

    if (!selectedProduct) {
      throw new Error('Selected service is no longer available');
    }

    // Check booking type availability
    const isOnDemand = !orderData.scheduled_at;
    const bookingAllowed = isOnDemand 
      ? selectedProduct.product.booking_settings.on_demand?.allowed
      : selectedProduct.product.booking_settings.pre_book?.allowed;

    if (!bookingAllowed) {
      throw new Error('Selected booking type is not available for this service');
    }

    // Get the estimation_id (quote_id) from the pricing options
    const quoteId = selectedProduct.price.pricing_options[0].estimation_id;

    // Proceed with order creation using validated product_id and quote_id
    return await this.createOrder({
      ...orderData,
      product_id: selectedProductId,
      quote_id: quoteId
    });
  }

  formatPriceDisplay(pricingOptions) {
    const pricing = pricingOptions[0];
    
    // Handle meter pricing - show as estimate
    if (pricing.price_concept === 'meter') {
      return {
        displayText: `${pricing.currency} Metered pricing`,
        type: 'meter',
        isEstimate: true,
        note: 'Final price determined by taxi meter'
      };
    }
    
    // Handle meter range pricing - show range estimate
    if (pricing.price_concept === 'meter_range') {
      const mainProperty = pricing.properties.find(p => p.name.includes('total'));
      if (mainProperty && mainProperty.is_range) {
        return {
          displayText: `${pricing.currency}${mainProperty.min}-${mainProperty.max}`,
          estimatedAmount: `${pricing.currency}${mainProperty.amount}`,
          type: 'meter_range',
          isEstimate: true,
          note: 'Estimated range - final price determined by taxi meter'
        };
      }
    }
    
    // Handle fixed pricing - show exact amount
    if (pricing.price_concept === 'fixed_price') {
      const mainProperty = pricing.properties.find(p => p.name.includes('total'));
      if (mainProperty && mainProperty.amount !== "0") {
        return {
          displayText: `${pricing.currency}${mainProperty.amount}`,
          type: 'fixed_price',
          isEstimate: false,
          note: 'Price guaranteed at booking'
        };
      }
    }
    
    // Fallback for other pricing types
    return {
      displayText: `${pricing.currency} Variable pricing`,
      type: 'variable',
      isEstimate: true,
      note: 'Contact support for pricing details'
    };
  }
}