FlyNDeal logo
Developer Platform FlyNDeal Partner API External integration docs for approved agencies
Quickstart Postman Open Swagger
FlyNDeal Partner API

Developer documentation for external agent portals

Connect your portal to FlyNDeal for inventory search, seat verification, booking creation, and booking tracking. This documentation is designed for external partner developers, not for internal admin users.

Base URL

https://flyndeal.com

Prefix

/api/b2b

Auth

x-api-key header

Authentication

All partner requests must send an API key in the x-api-key header.

Booking creation also requires a unique idempotency-key header so repeated booking requests can be safely replayed or rejected if the payload changes.

B2B_TICKETS_READ B2B_BOOKINGS_WRITE B2B_BOOKINGS_READ

Keep Server Side

Store the API key on your backend only. Do not expose it inside browser JavaScript.

Replay Safety

Every new booking attempt should use a fresh idempotency-key.

Minimum Stack

Your portal backend can be Node.js, Laravel, PHP, Python, or any server that can send HTTPS requests.

Integration flow

  1. Verify the API key and partner organization with GET /api/b2b/health.
  2. Load route filters with GET /api/b2b/sectors.
  3. Load airline filters with GET /api/b2b/airlines.
  4. Load grouped listing data with GET /api/b2b/tickets/grouped.
  5. Search ticket inventory with POST /api/b2b/tickets/search.
  6. Perform seat verification with POST /api/b2b/tickets/seats.
  7. Create the booking with POST /api/b2b/bookings.
  8. Track lifecycle status with GET /api/b2b/bookings/{bookingRef}.

For live FlyNDeal fares, always send the original searchContext during seat verification and booking so the fare can be revalidated accurately.

Step-by-step guide for a new portal

If a developer is building a portal from scratch, this is the safest and most practical way to integrate FlyNDeal without confusion.

Build Order

  1. Create a secure backend area to store the FlyNDeal API key.
  2. Build a grouped tickets page using GET /api/b2b/tickets/grouped.
  3. Build a search form using sectors, date, and passengers.
  4. Optionally load airlines for UI filters and logos.
  5. Call ticket search and render results.
  6. Store the chosen flightId and original searchContext.
  7. Verify seats before collecting final payment or confirming the fare.
  8. Collect passenger passport details accurately.
  9. Create the booking with a fresh idempotency-key.
  10. Save the returned bookingRef in your own database.
  11. Build a status page around GET /api/b2b/bookings/{bookingRef}.

Developer Rules

  • Keep the API key on your server, not in browser JavaScript.
  • Treat search results as short-lived inventory.
  • Always run seat verification before booking.
  • Reuse the same search context for live fares.
  • Handle 409 idempotency conflicts carefully.
  • Use bookingRef as the permanent FlyNDeal tracking reference.
  • Refresh search results if the fare can no longer be validated.

Testing sequence for developers

If the developer wants to confirm the integration in the safest order, test the API in this exact sequence before writing the full booking UI.

  1. Call GET /api/b2b/health and confirm the key is valid.
  2. Call GET /api/b2b/sectors and verify routes are loading.
  3. Call GET /api/b2b/airlines and verify airline catalog loads.
  4. Call GET /api/b2b/tickets/grouped and confirm grouped route blocks are loading.
  5. Call POST /api/b2b/tickets/search for a known sector/date.
  6. Copy one returned flightId.
  7. Call POST /api/b2b/tickets/seats with the same search context.
  8. Only after that, test POST /api/b2b/bookings.
  9. Finally test GET /api/b2b/bookings/{bookingRef}.

What your portal should save

Temporary Search Data

  • from
  • to
  • date
  • passengers
  • flightId

Permanent Booking Data

  • bookingRef
  • passenger names
  • passport details
  • selected fare summary
  • latest booking status

Health

GET /api/b2b/health

Returns integration status, organization identity, granted scopes, enabled capabilities for the current partner key.

Purpose

  • Verify the API key is active
  • Confirm the organization linked to the key
  • Inspect scopes and capabilities before integrating

Example Response

{
  "status": "success",
  "data": {
    "integration": {
      "status": "READY",
      "organization": {
        "name": "Motive Solutions",
        "tier": "SILVER"
      }
    },
    "scopes": [
      "B2B_TICKETS_READ",
      "B2B_BOOKINGS_READ",
      "B2B_BOOKINGS_WRITE"
    ]
  }
}

Sectors catalog

GET /api/b2b/sectors

Returns FlyNDeal-supported route options for partner dropdowns, quick filters, and prebuilt search forms.

When to use

  • Build your sector dropdowns
  • Preload route shortcuts on your search page
  • Cache safe route options in your own portal

Example Response

{
  "status": "success",
  "count": 2,
  "data": [
    {
      "from": "LHE",
      "to": "DXB",
      "routePath": "LHE-DXB",
      "label": "LHE -> DXB"
    }
  ]
}

Airlines catalog

GET /api/b2b/airlines

Returns the partner-safe airline catalog for filters, labels, and branding inside your portal.

When to use

  • Build airline filter lists
  • Show airline codes and names consistently
  • Render approved airline logos in your UI

Example Response

{
  "status": "success",
  "count": 2,
  "data": [
    {
      "id": "default-pf",
      "code": "PF",
      "name": "AirSial",
      "logoUrl": "https://cdn.example.com/airlines/airsial.webp"
    }
  ]
}

Grouped tickets

GET /api/b2b/tickets/grouped

Returns grouped FlyNDeal ticket inventory for listing pages, with route blocks and ticket rows underneath them.

Best for

  • Group tickets page
  • Route-based listing cards
  • Airline-filtered fare tables

Example Query

GET /api/b2b/tickets/grouped?subCategoryId=YOUR_SUBCATEGORY_UUID&page=1&pageSize=50

Supported Query Params

  • from, to, date
  • passengers
  • categoryId, subCategoryId, subSubCategoryId
  • airline
  • minPrice, maxPrice
  • lowSeatsOnly
  • page, pageSize

Important Notes

  • Use groups to render route sections.
  • Use rows inside each group for fare rows.
  • Provider internals are not exposed.
  • SkyPass seat counts are hidden where required by FlyNDeal policy.

Example Response

{
  "status": "success",
  "data": {
    "items": [
      {
        "id": "FND-CFA5F676962902EFEA0A4796",
        "type": "LIVE_QUOTE",
        "pnr": null,
        "airlineCode": "9P",
        "airlineName": "FLY JINNAH",
        "flightNo": "764",
        "from": "ISB",
        "to": "BAH",
        "routePath": "ISB-BAH",
        "itineraryType": "ONE_WAY",
        "departureDate": "2026-07-16",
        "departureTime": "21:10",
        "arrivalTime": "22:50:00",
        "seatsAvailable": 7,
        "hideSeats": false,
        "isRefundable": false,
        "sellingPrice": 114000,
        "currency": "PKR",
        "isStale": false
      }
    ],
    "groups": [
      {
        "groupKey": "ISB-BAH__9P",
        "routeKey": "ISB-BAH",
        "from": "ISB",
        "to": "BAH",
        "airlineCode": "9P",
        "airlineName": "FLY JINNAH",
        "summary": {
          "totalRows": 1,
          "minSellingPrice": 114000,
          "maxSellingPrice": 114000,
          "avgSellingPrice": 114000,
          "lowSeatsCount": 0
        },
        "rows": [
          {
            "id": "FND-CFA5F676962902EFEA0A4796",
            "flightNo": "764",
            "departureDate": "2026-07-16",
            "departureTime": "21:10",
            "arrivalTime": "22:50:00",
            "sellingPrice": 114000,
            "currency": "PKR"
          }
        ]
      }
    ],
    "meta": {
      "page": 1,
      "pageSize": 50,
      "returned": 1,
      "returnedGroups": 1,
      "hasNext": false,
      "hasPrev": false
    }
  }
}

Frontend Mapping

  • Render one card or section per group.
  • Use group.routeKey and group.airlineName in the header.
  • Use group.summary for fare count and min/avg price labels.
  • Loop over group.rows for the actual table rows.
  • Use items only if your portal also needs a flat list view.

Verify seats

POST /api/b2b/tickets/seats

Confirms seat availability before booking. This should always be done for live quotes to avoid stale inventory issues.

Live Quote Payload

{
  "flightId": "FND-86E34147AC02B960A66B7CAB",
  "passengers": 1,
  "searchContext": {
    "from": "LHE",
    "to": "DXB",
    "date": "2026-07-12"
  }
}

Example Response

{
  "status": "success",
  "data": {
    "available": true,
    "message": "Seats available on verified FlyNDeal fare",
    "seatsAvailable": 7
  }
}

Create booking

POST /api/b2b/bookings

Creates a partner booking request using validated inventory or a verified live quote. Each new booking attempt must use a fresh idempotency-key.

Required Headers

  • x-api-key
  • idempotency-key

Example Body

{
  "flightId": "FND-86E34147AC02B960A66B7CAB",
  "isUrgent": false,
  "passengers": [
    {
      "title": "MR",
      "passengerType": "ADULT",
      "firstName": "Ali",
      "lastName": "Khan",
      "passportNo": "P1234567",
      "nationality": "PK",
      "dob": "1995-01-01",
      "passportExpiry": "2031-01-01"
    }
  ],
  "searchContext": {
    "from": "LHE",
    "to": "DXB",
    "date": "2026-07-12"
  }
}

Example Response

{
  "status": "success",
  "message": "B2B booking request created successfully",
  "data": {
    "bookingRef": "FND-2E58CF997B4DD234",
    "status": "QUEUED",
    "pnr": "ABC123",
    "pnrConfirmation": null,
    "externalPNR": "ABC123",
    "supplierReference": "ABC123"
  }
}

Get booking status

GET /api/b2b/bookings/{bookingRef}

Returns booking status and financial summary for a booking that belongs to the authenticated partner organization. This is the authoritative polling endpoint for the latest supplier-synced PNR / reference value.

Read pnr first. If it is empty, fallback to pnrConfirmation || externalPNR || supplierReference.

Path Parameter

  • bookingRef: FlyNDeal booking reference

Example Response

{
  "status": "success",
  "data": {
    "bookingRef": "FND-2E58CF997B4DD234",
    "status": "QUEUED",
    "totalAmount": 21000,
    "pnr": "GDS123",
    "pnrConfirmation": null,
    "externalPNR": "GDS123",
    "supplierReference": "GDS123"
  }
}

Error model

Standard error responses follow a simple JSON shape with status and message. Validation failures may also include an errors array with field-level detail.

Partner endpoints are intentionally sanitized. Internal provider, supplier, and routing details are not exposed in this documentation or in runtime error messages.

Common Mistake

Trying to book directly after search without seat verification on a live fare.

Common Mistake

Reusing the same idempotency-key with a different payload.

Common Mistake

Exposing the API key in frontend source code or in public browser requests.

  • 200 successful read
  • 201 booking created
  • 400 invalid request or business rule failure
  • 401 invalid or insufficient-scope key
  • 404 quote or booking not found
  • 409 idempotency conflict
  • 422 booking failed after validation
  • 429 rate limit exceeded