MCP-native · SSE transport · HITLaaS fallback

RunRelay Flights API

Search and book flights from any AI agent.

One URL. Real prices. Human backup.

Base URL https://api.runrelay.io
9
MCP Tools
SSE
Transport
HITLaaS
Zero-drop booking
Getting Started

Quick Start

Up and running in under 5 minutes.

1

Get your API key

RunRelay Flights is currently in private beta. Reach out to get your API key and access credentials.

info@runrelay.io
2

Connect your MCP client

Add RunRelay to your MCP configuration file:

json — mcp config
{
  "mcpServers": {
    "runrelay-flights": {
      "url": "https://api.runrelay.io/sse",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}
3

Start searching

Ask your AI agent in natural language — RunRelay handles the rest:

Search

"Find me a flight from Barcelona to Lisbon on March 20"

Select & Book

"Book the first result, business class, window seat"

Add passengers

"Add my passport details and use my saved profile"

Protocol

Connection

RunRelay uses SSE transport over standard HTTP. No WebSockets required.

Endpoints

GET /sse Opens SSE stream, returns sessionId
POST /messages?sessionId=<id> JSON-RPC 2.0
GET /health No auth · status check
Transport: Server-Sent Events (SSE) — the server pushes responses; you POST messages. Each session gets a unique sessionId.

Authentication

Send your API key as a Bearer token in all requests (except /health):

http header
Authorization: Bearer YOUR_API_KEY
Keys are scoped to your account
All requests over TLS (HTTPS)
/health is public — no key needed

Session Flow

🔗
Connect SSE
GET /sse
🤝
Initialize
JSON-RPC initialize
🛠️
Call Tools
POST /messages
Receive Results
SSE push events
API Reference

Tools Reference

9 tools available via MCP. Relay Credits (RC) are consumed per call.

search_flights 2 RC

Search for available flights between airports

get_flight_details 1 RC

Get full fare details, baggage rules, and segments for an offer

get_seat_map 1 RC

Get cabin layout with seat availability and pricing

get_services 1 RC

Get available add-ons: extra baggage, meals, priority boarding

book_flight 25 RC HITLaaS

Book a flight — with automatic human operator escalation on failure

User & Traveler Management Free

4 tools for managing user profiles and saved travelers. No RC cost.

setup_user

Create a user profile with seat preferences, cabin class defaults, and confirmation style.

Params: userId, preferences (seatPreference, defaultCabin, confirmationStyle)
get_user_profile

Retrieve a user profile including preferences and full booking history.

Params: userId
update_user_profile

Update specific preferences without overwriting the full profile.

Params: userId, updates (partial)
add_traveler

Save a traveler with passport details and frequent flyer numbers. Once added, book_flight auto-fills passenger data.

Params: userId, traveler (name, passport, FFP)
💡 Tip: Use add_traveler once to store passport and frequent flyer details. All future book_flight calls will automatically populate passenger data — no need to re-enter details.
Architecture

How It Works

From natural language to boarding pass — powered by real GDS/NDC inventory.

1
AI agent connects via MCP

Your Claude, GPT, or custom agent connects to https://api.runrelay.io/sse using the MCP SSE transport.

2
User asks to find a flight

Natural language query triggers search_flights. RunRelay parses IATA codes, dates, and passenger counts automatically.

3
RunRelay searches real GDS/NDC inventory

Live pricing from global distribution systems. No cached fares, no bait-and-switch prices.

4
User selects and confirms

Agent can call get_flight_details, get_seat_map, and get_services to give the user full context before booking.

5
RunRelay books automatically

book_flight places the reservation, charges payment, and returns the PNR (booking reference).

6
Zero-drop execution via HITLaaS

If anything fails — airline API error, seat conflict, payment issue — a RunRelay human operator takes over and completes the booking manually. You're notified when done.

HITLaaS — Human-in-the-Loop as a Service

RunRelay's reliability guarantee

Airline APIs fail. Inventory disappears. Edge cases happen. When automated booking hits a wall, RunRelay's human operators receive an escalation ticket, complete the booking manually through airline portals, and notify your agent when complete — all transparently.

Auto
Normal booking
Human
On failure only

Supported Platforms

🖥️ Claude Desktop
OpenClaw
🎯 Cursor
🤖 Any MCP client
🔧 Custom integration via SSE
Pricing

Relay Credits

Pay per operation. Credits are consumed only on successful tool calls.

2
Relay Credits
Search Flights
search_flights
1
Relay Credit each
Details / Seat Map / Services
get_flight_details · get_seat_map · get_services
Free
No RC cost
User Management
setup · profile · traveler

Need volume pricing?

We offer custom plans for high-volume integrations and enterprise clients.

Contact Sales
Code Examples

Full Flow Examples

Complete end-to-end examples from connection to booking confirmation.

bash — step 1: connect SSE stream
# Connect to SSE and capture session ID
# Run in background; the server will emit sessionId in the first event
curl -N \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: text/event-stream" \
  https://api.runrelay.io/sse

# Example SSE response:
# data: {"type":"session","sessionId":"sess_abc123def456"}
bash — step 2: initialize MCP session
SESSION_ID="sess_abc123def456"

curl -X POST \
  "https://api.runrelay.io/messages?sessionId=$SESSION_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {
        "name": "my-agent",
        "version": "1.0.0"
      }
    }
  }'
bash — step 3: search for flights
curl -X POST \
  "https://api.runrelay.io/messages?sessionId=$SESSION_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "search_flights",
      "arguments": {
        "origin": "BCN",
        "destination": "LIS",
        "departureDate": "2025-03-20",
        "passengers": { "adults": 1, "children": 0, "infants": 0 },
        "cabinClass": "economy",
        "maxResults": 5
      }
    }
  }'

# Result arrives on your SSE stream:
# data: {"type":"result","id":2,"result":{"offers":[...]}}
bash — step 4: get full offer details
OFFER_ID="offer_abc123xyz"  # from search results

curl -X POST \
  "https://api.runrelay.io/messages?sessionId=$SESSION_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"id\": 3,
    \"method\": \"tools/call\",
    \"params\": {
      \"name\": \"get_flight_details\",
      \"arguments\": { \"offerId\": \"$OFFER_ID\" }
    }
  }"
bash — step 5: book the flight
curl -X POST \
  "https://api.runrelay.io/messages?sessionId=$SESSION_ID" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"id\": 4,
    \"method\": \"tools/call\",
    \"params\": {
      \"name\": \"book_flight\",
      \"arguments\": {
        \"offerId\": \"$OFFER_ID\",
        \"passengers\": [{
          \"type\": \"adult\",
          \"firstName\": \"Jane\",
          \"lastName\": \"Smith\",
          \"dateOfBirth\": \"1985-06-15\",
          \"passport\": {
            \"number\": \"AB123456\",
            \"expiryDate\": \"2030-01-01\",
            \"issuingCountry\": \"GB\"
          },
          \"email\": \"jane@example.com\",
          \"phone\": \"+44 7700 900000\"
        }],
        \"paymentMethod\": {
          \"type\": \"card\",
          \"savedMethodId\": \"pm_saved_123\"
        }
      }
    }
  }"

# Success response:
# { "bookingReference": "RR-BCN-LIS-20250320-XK7" }
#
# Or if escalated to human operator:
# { "escalationTicketId": "esc_789abc", "status": "pending_human" }
Uses @modelcontextprotocol/sdk — the official TypeScript SDK for MCP clients. Install with: npm install @modelcontextprotocol/sdk
typescript — full flow with MCP SDK
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";

const API_KEY = process.env.RUNRELAY_API_KEY!;
const BASE_URL = "https://api.runrelay.io";

async function bookFlight() {
  // ── 1. Create transport with auth ──────────────────────────────────
  const transport = new SSEClientTransport(new URL(`${BASE_URL}/sse`), {
    requestInit: {
      headers: {
        Authorization: `Bearer ${API_KEY}`,
      },
    },
  });

  // ── 2. Connect MCP client ──────────────────────────────────────────
  const client = new Client(
    { name: "my-flight-agent", version: "1.0.0" },
    { capabilities: {} }
  );

  await client.connect(transport);
  console.log("✅ Connected to RunRelay MCP");

  // ── 3. Search for flights ──────────────────────────────────────────
  const searchResult = await client.callTool({
    name: "search_flights",
    arguments: {
      origin: "BCN",
      destination: "LIS",
      departureDate: "2025-03-20",
      passengers: { adults: 1, children: 0, infants: 0 },
      cabinClass: "economy",
      maxResults: 5,
    },
  });

  const { offers } = searchResult.content[0] as { offers: any[] };
  console.log(`✈️  Found ${offers.length} flights`);

  if (offers.length === 0) {
    console.log("No flights found");
    return;
  }

  const bestOffer = offers[0];
  console.log(`   Best: ${bestOffer.airline} — €${bestOffer.totalPrice}`);

  // ── 4. Get full details for the selected offer ─────────────────────
  const details = await client.callTool({
    name: "get_flight_details",
    arguments: { offerId: bestOffer.offerId },
  });
  console.log("📋 Fare conditions:", details.content[0]);

  // ── 5. Check seat map (optional) ──────────────────────────────────
  try {
    const seatMap = await client.callTool({
      name: "get_seat_map",
      arguments: { offerId: bestOffer.offerId },
    });
    console.log("💺 Seat map available:", seatMap.content[0]);
  } catch {
    console.log("💺 Seat map not available for this airline");
  }

  // ── 6. Book the flight ─────────────────────────────────────────────
  const booking = await client.callTool({
    name: "book_flight",
    arguments: {
      offerId: bestOffer.offerId,
      passengers: [
        {
          type: "adult",
          firstName: "Jane",
          lastName: "Smith",
          dateOfBirth: "1985-06-15",
          passport: {
            number: "AB123456",
            expiryDate: "2030-01-01",
            issuingCountry: "GB",
          },
          email: "jane@example.com",
          phone: "+44 7700 900000",
        },
      ],
      paymentMethod: {
        type: "card",
        savedMethodId: "pm_saved_123",
      },
    },
  });

  const result = booking.content[0] as any;

  if (result.bookingReference) {
    console.log(`🎉 Booked! Reference: ${result.bookingReference}`);
  } else if (result.escalationTicketId) {
    // HITLaaS: human operator will complete the booking
    console.log(`🤝 Escalated to human operator: ${result.escalationTicketId}`);
    console.log("   You'll be notified when the booking is confirmed.");
  }

  await client.close();
}

bookFlight().catch(console.error);
typescript — user profile & saved travelers
// One-time setup: create user profile & add traveler
// After this, book_flight auto-fills passenger data!

// Create user profile
await client.callTool({
  name: "setup_user",
  arguments: {
    userId: "user_jane_smith",
    preferences: {
      seatPreference: "window",
      defaultCabin: "economy",
      confirmationStyle: "always_confirm", // ask before booking
      mealPreference: "vegetarian",
    },
  },
});

// Add a saved traveler (done once, reused forever)
await client.callTool({
  name: "add_traveler",
  arguments: {
    userId: "user_jane_smith",
    traveler: {
      firstName: "Jane",
      lastName: "Smith",
      dateOfBirth: "1985-06-15",
      passport: {
        number: "AB123456",
        expiryDate: "2030-01-01",
        issuingCountry: "GB",
        nationality: "GB",
      },
      frequentFlyer: [
        { airline: "IB", number: "IBE123456" }, // Iberia Plus
        { airline: "TP", number: "TP789012" },  // TAP Miles&Go
      ],
    },
  },
});

// Now book_flight will auto-fill Jane's details:
await client.callTool({
  name: "book_flight",
  arguments: {
    offerId: "offer_xyz",
    userId: "user_jane_smith", // passengers auto-populated!
    paymentMethod: { type: "card", savedMethodId: "pm_saved_123" },
  },
});
API is operational

Ready to build?

Get your API key and start searching real flights in minutes.