Skip to main content
Glama

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
NameRequiredDescriptionDefault
paramsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • 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}"
  • 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"
        )
  • 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
        }
    )
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds significant behavioral context beyond annotations: it discloses that this creates real bookings with payment implications, is typically non-refundable, and has specific response codes (201, 200, 202) with different meanings. Annotations provide basic hints (not read-only, not idempotent, etc.), but the description elaborates on real-world consequences like airline reservations and webhook notifications.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections (warning, prerequisites, required data, returns, response codes, testing note) and front-loaded critical information. Some redundancy exists (e.g., 'Returns order details' appears twice), but overall it's efficient with no wasted sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (real-world booking with payment), lack of schema descriptions, and presence of an output schema, the description is highly complete. It covers purpose, usage, behavioral risks, parameter expectations, response handling, and testing guidance, leaving minimal gaps for the agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage and 1 parameter (params referencing CreateOrderInput), the description compensates well by explaining required data: offer ID, passenger details (names, DOB, contact), and payment information. It doesn't detail the exact structure of CreateOrderInput but provides meaningful semantic context about what the parameter should contain.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Create a flight booking') and resource ('order for the specified offer and passengers'), distinguishing it from sibling tools like duffel_get_offer (read-only) and duffel_search_flights (search). It goes beyond the title by specifying it's for flight booking with offer and passenger inputs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly provides when to use (after verifying offer with duffel_get_offer, confirming passenger details, checking payment, and understanding terms) and when not to use (without test mode tokens to avoid charges). It names the alternative tool (duffel_get_offer) and includes prerequisites in a numbered list.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/FortripEngineering/duffel-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server