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 |
Output Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | 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 } )