Flyte API

Point an AI agent at live flight inventory. Plain language in, real fares out, over REST or MCP.

Search only. There is no booking API.

An agent using Flyte can find flights and read their prices. It cannot reserve, pay for, or ticket anything, and there is no endpoint or tool that does. This is not a gap we are about to fill on a timeline you can plan around, so please do not design a booking flow against it. Build search, and hand the traveller off to flyteai.io to complete a booking.

Getting a key

Keys are issued on request, not self-serve. Ask for one at /request-access. Tell us what you are building; we would rather know.

A key looks like flyte_sk_.... The prefix is there so that if one ever leaks into a repository or a log, whoever finds it can tell what it is.

The key is shown once and never again. We store only a hash, so we could not show it to you a second time even if you asked. Copy it when you get it. If you lose it, revoke it and take another.

Ten keys per account. Use a separate key per environment so you can revoke one without breaking the others. Once you are signed in you can list and revoke your own keys by their last four characters; revocation takes effect within a minute.

Authenticating

Send the key in an x-api-key header. HTTPS only; plain HTTP is redirected.

curl https://api.flyteai.io/api/environment \
  -H "x-api-key: flyte_sk_your_key_here"

Authorization: ApiKey flyte_sk_... works too, if your HTTP client insists on putting credentials there.

Prefer x-api-key on REST.

Authorization: Bearer flyte_sk_... is also accepted, but signed-in user sessions use the same header, so x-api-key is the clearer choice. The MCP endpoint is different; see below.

GET /api/health is the only endpoint that answers without a credential.

MCP server

Flyte speaks MCP, so an AI assistant can search flights as a tool call. JSON-RPC 2.0 over HTTP POST, protocol revision 2025-06-18.

MCP needs the key as a bearer token.

Send Authorization: Bearer flyte_sk_..., the one header most MCP clients can send. x-api-key alone returns 401 here. The config below sends both, which also works.

{
  "mcpServers": {
    "flyte": {
      "url": "https://api.flyteai.io/mcp",
      "headers": {
        "x-api-key": "flyte_sk_your_key_here",
        "Authorization": "Bearer flyte_sk_your_key_here"
      }
    }
  }
}

The server implements initialize, tools/list and tools/call, and advertises tools only: no resources, no prompts. There is exactly one tool.

{
  "name": "search_flights",
  "description": "Search live flight inventory using plain language...",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "The travel request in plain language, e.g. 'ORD to JFK on December 15th'."
      },
      "conversation_id": {
        "type": "string",
        "description": "Optional. Reuse the same id across calls to refine a search."
      }
    },
    "required": ["query"]
  }
}

A call, and what comes back:

curl -X POST https://api.flyteai.io/mcp \
  -H "x-api-key: flyte_sk_your_key_here" \
  -H "Authorization: Bearer flyte_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "search_flights",
      "arguments": {
        "query": "ORD to JFK on December 15th",
        "conversation_id": "my-agent-session-1"
      }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Flight Found!\n\nOUTBOUND - Tue, Dec 15, 2026\nORD to JFK\nDelta Air Lines DL 5035\n1:35 PM ORD - 5:05 PM JFK\n3h 30m - Direct\n\nCabin: Economy (Y class)\nRefundable: No\nBase Fare: USD 108.70\nTaxes & Fees: USD 24.00\nTotal: USD 132.70\n\n8 flights available - Showing best price"
      }
    ],
    "isError": false
  }
}

Two things to plan for. The tool returns text, not structured fields. If your agent needs a decimal price or a carrier code to work with, call the REST endpoint and read metadata.flightOffer instead. And the text may go on to ask for passenger details, because the same assistant powers the chat product on flyteai.io; over MCP there is nothing to send them to. Worth a line in your system prompt telling the model not to collect them.

Omitting conversation_id makes every call from your key share a single conversation. Pass your own id for anything concurrent.

When a search fails you get a normal successful result with isError: true and an explanation in the text, rather than a JSON-RPC error. The call worked; the search did not, and a model can read the difference.

Paying with USDC on Solana

Partner storefronts only, and off unless enabled.

This rail exists for storefronts whose travellers hold USDC on Solana. It is switched off by default. When it is off, payment requests offer Base only and the endpoint below returns 404.

When a conversation reaches payment, the response metadata has content_type: "payment_request". With the rail on it also carries paymentRails: ["base", "solana"] and a solana block. The amount is fixed by the server when the request opens. Send exactly amountAtomic of mint to escrowTokenAccount, signed by the traveller's own wallet.

"solana": {
  "network": "solana",
  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "escrowAddress": "<escrow owner>",
  "escrowTokenAccount": "<escrow USDC token account>",
  "amount": "412.60",
  "amountAtomic": 412600000,
  "decimals": 6,
  "claimEndpoint": "/api/payment-success/solana/start"
}

Once the transfer is sent, claim it. The traveller must be signed in: send their Flyte session as the bearer token and the identity token from their wallet sign-in in the body. The Solana wallet must be linked to the same account as the wallet that started the conversation.

curl -X POST https://api.flyteai.io/api/payment-success/solana/start \
  -H "x-api-key: flyte_sk_your_key_here" \
  -H "Authorization: Bearer <flyte session>" \
  -H "Content-Type: application/json" \
  -d '{"conversation_id": "conv-123",
       "signature": "<base58 transaction signature>",
       "payer": "<traveller's Solana address>",
       "identity_token": "<sign-in identity token>"}'

# 202 {"status": "pending", "payment_reference": "sol_..."}

Then poll GET /api/payment-status with conversation_id and tx_hash=<payment_reference> until the status is not pending. Only booked means a ticket. A payment is checked only once the transaction is finalized, which takes around 15 seconds. A wrong token, the wrong recipient or a transfer from another wallet is not a payment. A wrong amount is refunded to the wallet it came from. If the trip was already paid on any rail, the second payment is refunded.

400 malformed signature or address, 401 identity token not valid, 403 wallet not linked to this account or a different Solana wallet is already paying, 409 no payment requested or this signature was already used. Every refusal happens before the signature is claimed, so it can be presented again once fixed.

Rate limits and errors

POST /api/process and POST /api/process/stream allow 30 requests per minute. POST /api/reset-conversation allows 10. Every limited response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; read them rather than guessing.

Limits are counted per IP address, not per key. If you are behind a shared egress address you are sharing a budget with whoever else is there.

POST /mcp is not rate limited today. Please do not treat that as an invitation: every call is a live inventory search, and an agent in a retry loop is the thing most likely to get a key revoked.

  • 401: key missing, malformed, revoked or expired. On MCP, also a missing bearer token
  • 422: the request body did not validate
  • 429: rate limited. Back off; do not loop
  • 500: the search failed. Usually transient; retry once, then stop

Over MCP, protocol mistakes come back inside a 200 as JSON-RPC errors: -32700 for unparseable JSON, -32600 if jsonrpc is not "2.0", -32601 for an unknown method, and -32602 for an unknown tool name or a missing query.

Things worth knowing before you build

  • Booking is not available through the API or MCP. Search returns real, live fares; nothing here issues a ticket or moves money.
  • Adult passengers only. Our certification with the inventory provider covers the adult passenger type. Search will not warn you about a trip involving children. The check happens later, in the booking flow on flyteai.io, which refuses it.
  • Fares move. A result is a quote at a moment, not a held price. Re-search rather than caching results, and never show a stored price as though it were still live.
  • Baggage allowances are often absent. The baggage fields come back null more often than not. Do not present them as authoritative.