In the modern airline industry, processing millions of transactions daily—ranging from standard ticket bookings and baggage fees to last-minute flight upgrades and massive corporate refunds—is a massive operational challenge. A single point of failure or a delayed fraud check can result in lost revenue, poor customer experience, or severe security breaches.

Enter LangGraph, a powerful framework for building stateful, multi-actor applications with Large Language Models (LLMs). By utilizing Conditional Edges, we can build an intelligent routing system that dynamically evaluates the risk of an airline transaction in real-time and routes it to the appropriate specialized AI agent.

In this end-to-end article, we will build a real-time Airline Transaction Risk Router. We will assess incoming transactions and route them to a Standard Processing Agent, an Enhanced Verification Agent, or a Fraud Investigation Agent based on their calculated risk level.

Prerequisites

Before we dive into the code, ensure you have the following set up:

1. Python Environment

You need Python 3.9 or higher. It is highly recommended to use a virtual environment.

2. Required Libraries

Install the necessary LangChain and LangGraph packages via pip:

pip install langgraph langchain langchain-openai pydantic

3. API Keys

We will use OpenAI's models for our agents. You need an OpenAI API key. Set it as an environment variable in your terminal or .env file:

export OPENAI_API_KEY="your-openai-api-key"

4. Conceptual Understanding

The Airline Use Case: Real-Time Transaction Routing

Imagine you are the Chief Technology Officer of "SkyHigh Airlines." Your payment gateway receives thousands of requests per minute.

The Scenarios:

  1. Low Risk: A frequent flyer purchases a $50 extra baggage fee using a saved credit card. -> Needs instant approval.

  2. Medium Risk: A user attempts a $2,500 last-minute upgrade to First Class from an IP address in a different country. -> Needs enhanced verification (e.g., 3D Secure or SMS OTP).

  3. High Risk: A suspicious pattern of 10 refund requests totaling $50,000 within 5 minutes from a newly created account. -> Needs immediate freezing and fraud investigation.

Instead of writing hundreds of rigid if/else rules, we will build an Agentic Workflow where an Assessment Node evaluates the context, and Conditional Edges route the transaction to the correct specialized AI agent.

Step-by-Step Implementation

Step 1: Define the State

In LangGraph, the State is the shared memory that passes between nodes. We will use Pydantic to strictly define our transaction data.

from typing import TypedDict, Literal
from pydantic import BaseModel, Field

class TransactionState(TypedDict):
    transaction_id: str
    passenger_name: str
    amount: float
    transaction_type: str
    context: str  # Additional context like IP, account age, etc.
    
    # Fields populated by the agents
    risk_level: Literal["low", "medium", "high"]
    risk_reasoning: str
    agent_action: str
    final_status: str

Step 2: Create the Nodes (The Agents)

Nodes are simply Python functions that take the current State, perform an action (like calling an LLM or a database), and return an updated State.

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

llm = ChatOpenAI(model="gpt-4o", temperature=0)

# 1. Risk Assessment Node
def assess_risk_node(state: TransactionState):
    prompt = ChatPromptTemplate.from_template(
        """You are an expert airline fraud detection AI. 
        Analyze the following transaction and assign a risk level: 'low', 'medium', or 'high'.
        
        Transaction ID: {transaction_id}
        Passenger: {passenger_name}
        Amount: ${amount}
        Type: {transaction_type}
        Context: {context}
        
        Respond ONLY in the following JSON format:
        {{
            "risk_level": "low/medium/high",
            "reasoning": "Brief explanation of the risk"
        }}
        """
    )
    
    # Using structured output for reliable parsing
    structured_llm = llm.with_structured_output(
        schema={
            "type": "object",
            "properties": {
                "risk_level": {"type": "string", "enum": ["low", "medium", "high"]},
                "reasoning": {"type": "string"}
            },
            "required": ["risk_level", "reasoning"]
        }
    )
    
    chain = prompt | structured_llm
    result = chain.invoke({
        "transaction_id": state["transaction_id"],
        "passenger_name": state["passenger_name"],
        "amount": state["amount"],
        "transaction_type": state["transaction_type"],
        "context": state["context"]
    })
    
    return {
        "risk_level": result["risk_level"],
        "risk_reasoning": result["reasoning"]
    }

# 2. Specialized Processing Agents
def standard_processing_node(state: TransactionState):
    return {
        "agent_action": "Standard Agent: Instantly approved. No friction applied.",
        "final_status": "APPROVED"
    }

def enhanced_verification_node(state: TransactionState):
    return {
        "agent_action": "Enhanced Agent: Triggering 3D Secure / SMS OTP verification.",
        "final_status": "PENDING_VERIFICATION"
    }

def fraud_investigation_node(state: TransactionState):
    return {
        "agent_action": "Fraud Agent: Transaction blocked. Account flagged for manual review.",
        "final_status": "BLOCKED"
    }

Step 3: Define the Routing Logic (The Conditional Edge)

This is the core of the tutorial. We write a function that inspects the State and returns a string. This string tells LangGraph which path to take.

def route_by_risk(state: TransactionState) -> Literal["standard", "enhanced", "fraud"]:
    """Determines the next node based on the assessed risk level."""
    risk = state.get("risk_level", "low")
    
    if risk == "high":
        return "fraud"
    elif risk == "medium":
        return "enhanced"
    else:
        return "standard"

Step 4: Build and Compile the Graph

Now we wire everything together using LangGraph's StateGraph.

from langgraph.graph import StateGraph, START, END

# Initialize the graph with our state schema
workflow = StateGraph(TransactionState)

# Add Nodes
workflow.add_node("assess_risk", assess_risk_node)
workflow.add_node("standard", standard_processing_node)
workflow.add_node("enhanced", enhanced_verification_node)
workflow.add_node("fraud", fraud_investigation_node)

# Add Edges
# 1. Entry point always goes to risk assessment
workflow.add_edge(START, "assess_risk")

# 2. The Conditional Edge: Routes from 'assess_risk' based on the routing function
workflow.add_conditional_edges(
    "assess_risk",           # Source node
    route_by_risk,           # Routing function
    {
        "standard": "standard", # Maps return value "standard" to node "standard"
        "enhanced": "enhanced",
        "fraud": "fraud"
    }
)

# 3. All processing agents eventually end the workflow
workflow.add_edge("standard", END)
workflow.add_edge("enhanced", END)
workflow.add_edge("fraud", END)

# Compile the graph into an executable application
app = workflow.compile()

Step 5: Execute and Test in Real-Time

Let's simulate three real-time transactions hitting our airline's payment gateway.

def run_transaction(transaction_data):
    print(f"\n--- Processing Transaction: {transaction_data['transaction_id']} ---")
    # Invoke the graph
    final_state = app.invoke(transaction_data)
    
    print(f"Risk Level: {final_state['risk_level'].upper()}")
    print(f"Reasoning: {final_state['risk_reasoning']}")
    print(f"Action: {final_state['agent_action']}")
    print(f"Final Status: {final_state['final_status']}")
    print("-" * 60)

# Test Case 1: Low Risk
txn1 = {
    "transaction_id": "TXN-001",
    "passenger_name": "John Doe",
    "amount": 45.00,
    "transaction_type": "Extra Baggage",
    "context": "Frequent flyer, saved credit card, domestic flight."
}

# Test Case 2: Medium Risk
txn2 = {
    "transaction_id": "TXN-002",
    "passenger_name": "Jane Smith",
    "amount": 2500.00,
    "transaction_type": "First Class Upgrade",
    "context": "Account created 2 days ago, IP address from different country than billing."
}

# Test Case 3: High Risk
txn3 = {
    "transaction_id": "TXN-003",
    "passenger_name": "Unknown User",
    "amount": 15000.00,
    "transaction_type": "Bulk Refund Request",
    "context": "10 refund requests in 5 minutes, newly created account, mismatched IP."
}

# Run the simulations
run_transaction(txn1)
run_transaction(txn2)
run_transaction(txn3)

Expected Output

When you run the script, the LLM will analyze the context and route the transactions dynamically:

--- Processing Transaction: TXN-001 ---
Risk Level: LOW
Reasoning: Low amount, frequent flyer, saved payment method.
Action: Standard Agent: Instantly approved. No friction applied.
Final Status: APPROVED
------------------------------------------------------------

--- Processing Transaction: TXN-002 ---
Risk Level: MEDIUM
Reasoning: High value upgrade, new account, IP mismatch.
Action: Enhanced Agent: Triggering 3D Secure / SMS OTP verification.
Final Status: PENDING_VERIFICATION
------------------------------------------------------------

--- Processing Transaction: TXN-003 ---
Risk Level: HIGH
Reasoning: Extremely high value, multiple rapid refunds, new account.
Action: Fraud Agent: Transaction blocked. Account flagged for manual review.
Final Status: BLOCKED
------------------------------------------------------------
18

Why This Architecture Matters for Airlines

  1. Dynamic Adaptability: Unlike hardcoded rules, the LLM can understand nuanced context (e.g., "IP mismatch" vs "User is traveling"). If you need to change the risk policy, you just update the prompt, not the core routing code.

  2. Separation of Concerns: The routing logic is completely decoupled from the execution logic. The Assessment Agent only cares about risk; the Fraud Agent only cares about security protocols.

  3. Scalability: You can easily add a fourth node (e.g., Corporate_Account_Node) and simply add a new condition to the route_by_risk function without breaking existing workflows.

  4. Customer Experience: Low-risk transactions experience zero friction, ensuring high conversion rates for ancillary revenue (baggage, seats), while high-risk transactions are caught before revenue leakage occurs.

By leveraging LangGraph's conditional edges, airlines can transform their payment gateways from rigid, rule-based bottlenecks into intelligent, adaptive, and highly secure agentic networks.