duffel_create_order
Create confirmed flight bookings with airline reservation, payment processing, and ticket issuance using verified offer details and passenger information.
Instructions
Create a flight booking (order) for the specified offer and passengers.
⚠️ IMPORTANT: This creates a real booking and may involve payment. This action:
- Creates a confirmed airline reservation
- May charge the payment method (if not using test mode)
- Issues a booking reference from the airline
- Is typically non-refundable or has cancellation fees
Before calling this tool:
1. Verify the offer is current using duffel_get_offer
2. Confirm all passenger details are accurate (names match IDs)
3. Check payment amount matches offer total
4. Ensure user understands booking terms
Required data:
- Offer ID from search results
- Complete passenger details (names as on ID, date of birth, contact info)
- Payment information matching offer total
The tool returns:
- Order ID for reference
- Airline booking reference
- Confirmation details
- Ticket information
Response codes:
- 201: Order created successfully (immediate confirmation)
- 200: Booking confirmed, details arriving soon (webhook notification will follow)
- 202: Booking processing (check webhooks or email for confirmation)
Use test mode tokens for testing to avoid actual charges.
Returns order details in specified format (JSON or Markdown).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Implementation Reference
- duffel_mcp/tools/flights.py:190-358 (handler)Complete handler function for duffel_create_order tool. Registers the tool with @mcp.tool, validates input via CreateOrderInput, constructs order data from parameters, makes POST request to Duffel API /air/orders endpoint, handles different response status codes (201,200,202), formats output in JSON or Markdown, and provides comprehensive error troubleshooting.@mcp.tool( name="duffel_create_order", annotations={ "title": "Create Flight Order (Book Flight)", "readOnlyHint": False, "destructiveHint": False, "idempotentHint": False, "openWorldHint": True } ) async def create_order(params: CreateOrderInput) -> str: """ Create a flight booking (order) for the specified offer and passengers. ⚠️ IMPORTANT: This creates a real booking and may involve payment. This action: - Creates a confirmed airline reservation - May charge the payment method (if not using test mode) - Issues a booking reference from the airline - Is typically non-refundable or has cancellation fees Before calling this tool: 1. Verify the offer is current using duffel_get_offer 2. Confirm all passenger details are accurate (names match IDs) 3. Check payment amount matches offer total 4. Ensure user understands booking terms Required data: - Offer ID from search results - Complete passenger details (names as on ID, date of birth, contact info) - Payment information matching offer total The tool returns: - Order ID for reference - Airline booking reference - Confirmation details - Ticket information Response codes: - 201: Order created successfully (immediate confirmation) - 200: Booking confirmed, details arriving soon (webhook notification will follow) - 202: Booking processing (check webhooks or email for confirmation) Use test mode tokens for testing to avoid actual charges. Returns order details in specified format (JSON or Markdown). """ order_data = { "data": { "selected_offers": [params.offer_id], "passengers": [ { "id": pax.id, "given_name": pax.given_name, "family_name": pax.family_name, "born_on": pax.born_on, "email": pax.email, "phone_number": pax.phone_number, "gender": pax.gender, **({"title": pax.title} if pax.title else {}) } for pax in params.passengers ], "payments": [ { "type": payment.type.value, "amount": payment.amount, "currency": payment.currency.upper() } for payment in params.payments ] } } try: response = await make_api_request( method="POST", endpoint="/air/orders", data=order_data ) status_code = response.get("meta", {}).get("status", 201) if status_code == 200: message = response.get("data", {}).get("message", "") return ( f"✅ Booking Confirmed!\n\n" f"{message}\n\n" f"The complete booking details will be available shortly. " f"You will receive:\n" f"- A webhook notification when the order is created\n" f"- An email to your support contact address\n\n" f"**Offer ID**: `{params.offer_id}`\n" f"Please check your webhooks or email for the order ID and booking reference." ) elif status_code == 202: return ( f"⏳ Booking Processing\n\n" f"Your booking request has been accepted and is being processed. " f"You will receive confirmation via:\n" f"- Webhook notification (order.created or order.creation_failed)\n" f"- Email to your support contact address\n\n" f"**Offer ID**: `{params.offer_id}`\n" f"This typically takes a few minutes. Please do not retry this request." ) else: order = response["data"] if params.response_format == ResponseFormat.JSON: return truncate_text(format_json_response(order)) else: # Markdown format lines = [ "# ✅ Booking Confirmed!", "", f"**Order ID**: `{order['id']}`", f"**Booking Reference**: {order.get('booking_reference', 'N/A')}", f"**Total**: {format_currency(order['total_amount'], order['total_currency'])}", "", "## Passengers" ] for i, pax in enumerate(order.get("passengers", []), 1): lines.append(f"{i}. {pax.get('given_name')} {pax.get('family_name')}") lines.append("") lines.append("## Flight Details") for i, slice_info in enumerate(order.get("slices", []), 1): origin = slice_info["origin"]["iata_code"] destination = slice_info["destination"]["iata_code"] lines.append(f"**Slice {i}**: {origin} → {destination}") lines.append(f"- Departure: {slice_info['departure_at']}") lines.append(f"- Arrival: {slice_info['arrival_at']}") lines.append("") lines.append("---") lines.append("*Save the Order ID and Booking Reference for future reference.*") return truncate_text("\n".join(lines)) except Exception as e: error_msg = str(e) troubleshooting = "\n\nTroubleshooting:\n" if "offer_expired" in error_msg: troubleshooting += "- The offer has expired. Perform a new flight search to get current offers.\n" elif "offer_no_longer_available" in error_msg: troubleshooting += "- The offer is no longer available. Search again or select a different offer.\n" elif "price_changed" in error_msg: troubleshooting += "- The price has changed. Retrieve the offer again to get updated pricing.\n" elif "validation" in error_msg.lower(): troubleshooting += "- Check passenger details are complete and accurate\n" troubleshooting += "- Verify names match government ID exactly\n" troubleshooting += "- Ensure date of birth is in YYYY-MM-DD format\n" troubleshooting += "- Confirm email and phone number are valid\n" elif "payment" in error_msg.lower(): troubleshooting += "- Verify payment amount matches offer total_amount exactly\n" troubleshooting += "- Confirm currency matches offer total_currency\n" troubleshooting += "- Check payment type is appropriate for your account\n" else: troubleshooting += "- Verify all passenger details are accurate\n" troubleshooting += "- Ensure payment amount and currency match the offer\n" troubleshooting += "- Check if using test mode token for testing\n" return f"Error creating order: {error_msg}{troubleshooting}"
- duffel_mcp/models/flights.py:194-219 (schema)Pydantic schema defining input parameters for duffel_create_order: offer_id, list of passengers with full details (PassengerDetails), payments (Payment), and optional response_format. Includes validation patterns, constraints, and descriptions for MCP tool schema.class CreateOrderInput(BaseModel): """Input for creating a flight order (booking).""" model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, extra='forbid') offer_id: str = Field( ..., description="Offer ID to book (e.g., 'off_00009htYpSCXrwaB9DnUm0')", pattern="^off_[a-zA-Z0-9]+$" ) passengers: list[PassengerDetails] = Field( ..., description="Complete details for all passengers", min_length=1, max_length=9 ) payments: list[Payment] = Field( ..., description="Payment information (typically one payment object)", min_length=1, max_length=1 ) response_format: ResponseFormat = Field( default=ResponseFormat.MARKDOWN, description="Output format: 'json' for raw data or 'markdown' for readable summary" )
- duffel_mcp/tools/flights.py:190-199 (registration)MCP tool registration decorator specifying the tool name 'duffel_create_order' and annotations for UI hints (not read-only, not destructive, not idempotent).@mcp.tool( name="duffel_create_order", annotations={ "title": "Create Flight Order (Book Flight)", "readOnlyHint": False, "destructiveHint": False, "idempotentHint": False, "openWorldHint": True } )