{
  "openapi": "3.1.0",
  "info": {
    "title": "Airdesk Partner API",
    "version": "2026-10-01",
    "summary": "Show head office's group fares and Umrah packages on your own site or system, and send customer requests back.",
    "description": "The Partner API lets an approved agency read the catalogue it is allowed to sell, send customer requests to head office, and follow what happens to them.\n\n## Getting access\n1. Sign in to the agent portal and open **Website & API**. Request access and choose a mode.\n2. Create **test keys** straight away: they read the real catalogue, but nothing you create with them reaches head office.\n3. When head office approves, accept the terms and create **live keys**. A key is shown once, so store it in a secrets manager.\n\n## Two modes\n- **Direct (website):** requests go straight to head office, tagged with your agency. Use a browser key from your site, or a secret key from your server.\n- **Connected (your system):** requests wait in your inbox as `RECEIVED`; you forward or decline them (in the portal or with `POST /requests/{id}/forward` and `/reject`), and you are told about every change by webhook or by polling `/events`.\n\n## Keys and authentication\nSend the key as `Authorization: Bearer <key>` (a browser key may instead use the `X-Airdesk-Key` header). **Never put a key in the URL.**\n\n| | Browser key `adk_pk_…` | Secret key `adk_sk_…` |\n|---|---|---|\n| Lives in | your website's JavaScript | your server only |\n| Can do | read the catalogue (retail prices, banded availability), create requests (with a Turnstile token) | everything its scopes allow, including net prices, reading and cancelling requests, events |\n| Origin | required, and must be on your allowed list | must **not** be sent (a secret key in a browser is treated as leaked) |\n\n`_test_` keys use the sandbox; `_live_` keys are real. Revoking a key takes effect on the very next request.\n\n## Conventions\n- **Envelope.** Success: `{ \"success\": true, \"data\": … }`. Error: `{ \"success\": false, \"error\": { \"code\", \"message\", \"requestId\", \"details?\" } }`. Branch on `error.code` (see the error catalogue below), never on the message.\n- **Money** is a decimal **string** with an ISO-4217 `currency`, never a float. Prices are in the inventory currency; there is no conversion.\n- **Pagination** is cursor based: pass `nextCursor` back as `cursor`. There is no total count. Feeds use `after` (a sequence number).\n- **Idempotency.** Every `POST` needs an `Idempotency-Key` (1-128 visible ASCII characters). Retrying the same call returns the stored answer with `200` and `Idempotent-Replayed: true`; reusing a key with a different body is `409`. Keys are remembered for 24 hours.\n- **Caching.** Catalogue responses carry an `ETag`; send `If-None-Match` to get `304`. Browser-key responses may be cached by shared caches (`max-age=60`); secret-key responses are `private, no-store`.\n- **Rate limits** are per key (browser keys also per client IP). Responses carry `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`; a `429` carries `Retry-After`.\n- **Versioning.** Every response carries `Airdesk-Api-Version`. Additive changes (new fields, new event types, new enum values) are not versioned: **ignore fields you do not know**. Breaking changes ship under `/v2/partner` with at least six months of overlap.\n- **Status** values are the same everywhere: `UNDER_REVIEW`, `HELD`, `CONFIRMED`, `FULFILLED`, `REJECTED`, `CANCELLATION_REQUESTED`, `CANCELLED`, `EXPIRED` (and, in connected mode, `RECEIVED`, `AGENT_REJECTED`). Internal statuses are never exposed.\n\n## Webhooks\nWebhooks are registered in the portal, not through this API. See the **Webhooks** section below for the payload, headers and how to verify a signature.",
    "contact": {
      "name": "Airdesk partner support"
    }
  },
  "servers": [
    {
      "url": "https://api.example.com",
      "description": "This deployment"
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "Check a key and see what it can do."
    },
    {
      "name": "Catalogue",
      "description": "Group fares and Umrah packages you may sell. Prices follow your markup."
    },
    {
      "name": "Requests",
      "description": "Customer requests to head office and their lifecycle."
    },
    {
      "name": "Events",
      "description": "An ordered feed of what happened, and read-only webhook information."
    }
  ],
  "security": [
    {
      "bearerKey": []
    }
  ],
  "paths": {
    "/v1/partner/me": {
      "get": {
        "tags": ["Account"],
        "operationId": "getMe",
        "summary": "Who am I",
        "description": "The integration, mode, environment and scopes of the calling key.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Me"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "integration": {
                      "id": "e187b4eb-cea4-46ed-a388-be65486d926a",
                      "name": "Example Travels website",
                      "mode": "DIRECT"
                    },
                    "key": {
                      "id": "ab15cc52-36b8-44ce-9e30-ca106538542c",
                      "type": "secret",
                      "environment": "live"
                    },
                    "scopes": [
                      "catalogue:read",
                      "requests:create",
                      "requests:read",
                      "requests:manage",
                      "events:read"
                    ],
                    "apiVersion": "2026-10-01",
                    "rateLimits": {
                      "reads": "600/min",
                      "writes": "60/min"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/ping": {
      "get": {
        "tags": ["Account"],
        "operationId": "ping",
        "summary": "Check a key",
        "description": "A cheap authenticated call for setup screens.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "const": true
                        },
                        "environment": {
                          "enum": ["live", "test"]
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "ok": true,
                    "environment": "live"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/catalogue/groups": {
      "get": {
        "tags": ["Catalogue"],
        "operationId": "listGroups",
        "summary": "List group fares",
        "description": "Active groups with seats left, minus any your agreement excludes. Needs scope `catalogue:read`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{3}$"
            },
            "description": "Departure airport (IATA).",
            "example": "KHI"
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z]{3}$"
            },
            "description": "Arrival airport (IATA).",
            "example": "JED"
          },
          {
            "name": "departureFrom",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Earliest first departure."
          },
          {
            "name": "departureTo",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Latest first departure."
          },
          {
            "name": "airline",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{2,3}$"
            },
            "description": "Airline code.",
            "example": "PK"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": ["data", "nextCursor"],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Group"
                          }
                        },
                        "nextCursor": {
                          "type": ["string", "null"]
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "data": [
                      {
                        "id": "4dece828-160a-4782-bea7-1c20ae185c29",
                        "code": "KHI-JED-0412",
                        "name": "Karachi to Jeddah, 12 Dec",
                        "airline": {
                          "code": "PK",
                          "name": "Pakistan International",
                          "logoUrl": null
                        },
                        "tripPattern": "ROUND_TRIP",
                        "currency": "PKR",
                        "refundable": true,
                        "paymentDeadlineHours": 24,
                        "availability": {
                          "status": "available"
                        },
                        "segments": [
                          {
                            "order": 1,
                            "direction": "OUTBOUND",
                            "flightNumber": "PK741",
                            "airlineCode": "PK",
                            "origin": "KHI",
                            "destination": "JED",
                            "departureAt": "2026-12-12T03:40:00.000Z",
                            "arrivalAt": "2026-12-12T07:10:00.000Z",
                            "aircraftType": "B777",
                            "baggage": {
                              "weightKg": 30,
                              "services": ["refundable"]
                            }
                          }
                        ],
                        "cabins": [
                          {
                            "id": "0f6c2d1e-6a3b-4f6e-9b1a-2c7d9e4a1b10",
                            "code": "Y",
                            "name": "Economy",
                            "prices": {
                              "adult": {
                                "amount": "110000.00",
                                "currency": "PKR"
                              },
                              "child": {
                                "amount": "88000.00",
                                "currency": "PKR"
                              }
                            },
                            "availability": {
                              "status": "limited"
                            }
                          }
                        ]
                      }
                    ],
                    "nextCursor": null
                  }
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Request-Id": {
                "description": "Quote this in support requests. You may send your own UUID in the request.",
                "schema": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/catalogue/groups/{groupId}": {
      "get": {
        "tags": ["Catalogue"],
        "operationId": "getGroup",
        "summary": "One group",
        "description": "Segments, baggage, and cabins with a price and availability per passenger type.",
        "parameters": [
          {
            "name": "groupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Group"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "4dece828-160a-4782-bea7-1c20ae185c29",
                    "code": "KHI-JED-0412",
                    "name": "Karachi to Jeddah, 12 Dec",
                    "airline": {
                      "code": "PK",
                      "name": "Pakistan International",
                      "logoUrl": null
                    },
                    "tripPattern": "ROUND_TRIP",
                    "currency": "PKR",
                    "refundable": true,
                    "paymentDeadlineHours": 24,
                    "availability": {
                      "status": "available"
                    },
                    "segments": [
                      {
                        "order": 1,
                        "direction": "OUTBOUND",
                        "flightNumber": "PK741",
                        "airlineCode": "PK",
                        "origin": "KHI",
                        "destination": "JED",
                        "departureAt": "2026-12-12T03:40:00.000Z",
                        "arrivalAt": "2026-12-12T07:10:00.000Z",
                        "aircraftType": "B777",
                        "baggage": {
                          "weightKg": 30,
                          "services": ["refundable"]
                        }
                      }
                    ],
                    "cabins": [
                      {
                        "id": "0f6c2d1e-6a3b-4f6e-9b1a-2c7d9e4a1b10",
                        "code": "Y",
                        "name": "Economy",
                        "prices": {
                          "adult": {
                            "amount": "110000.00",
                            "currency": "PKR"
                          },
                          "child": {
                            "amount": "88000.00",
                            "currency": "PKR"
                          }
                        },
                        "availability": {
                          "status": "limited"
                        }
                      }
                    ]
                  }
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/catalogue/packages": {
      "get": {
        "tags": ["Catalogue"],
        "operationId": "listPackages",
        "summary": "List Umrah packages",
        "description": "Published packages you may resell, with your retail prices. Needs scope `catalogue:read`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}$"
            },
            "description": "Month of an open departure.",
            "example": "2026-12"
          },
          {
            "name": "durationNights",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365
            }
          },
          {
            "name": "departureCity",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "LHE"
          },
          {
            "name": "tier",
            "in": "query",
            "schema": {
              "enum": ["ECONOMY", "STANDARD", "PREMIUM", "LUXURY"]
            }
          },
          {
            "name": "maxRetailPrice",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{1,12}(\\.\\d{1,2})?$"
            },
            "description": "Highest from-price, in your retail terms."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": ["data", "nextCursor"],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PackageListItem"
                          }
                        },
                        "nextCursor": {
                          "type": ["string", "null"]
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "data": [
                      {
                        "id": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                        "slug": "ramadan-economy-14n",
                        "title": "Ramadan Economy, 14 nights",
                        "summary": "Makkah and Madinah, shared quad rooms",
                        "kind": "FIXED_DEPARTURE",
                        "tier": "ECONOMY",
                        "totalNights": 14,
                        "makkahNights": 8,
                        "madinahNights": 6,
                        "departureCities": ["LHE"],
                        "currency": "PKR",
                        "featured": true,
                        "heroImageUrl": "https://assets.example.com/packages/ramadan.jpg",
                        "hotels": [
                          {
                            "city": "MAKKAH",
                            "nights": 8,
                            "mealPlan": "BREAKFAST",
                            "name": "Al Safwah",
                            "rating": 4,
                            "distanceToHaramM": 300
                          }
                        ],
                        "fromPrice": {
                          "amount": "198000.00",
                          "currency": "PKR"
                        },
                        "nextDeparture": {
                          "id": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                          "departureDate": "2026-12-20",
                          "returnDate": "2027-01-03",
                          "departureCity": "LHE",
                          "airlineCode": "PK",
                          "availability": {
                            "status": "available"
                          }
                        },
                        "availability": {
                          "status": "available"
                        }
                      }
                    ],
                    "nextCursor": null
                  }
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/catalogue/packages/{packageIdOrSlug}": {
      "get": {
        "tags": ["Catalogue"],
        "operationId": "getPackage",
        "summary": "One package",
        "description": "Hotels, itinerary, inclusions and exclusions, terms, media and departures.",
        "parameters": [
          {
            "name": "packageIdOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "ramadan-economy-14n"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/PackageDetail"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "slug": "ramadan-economy-14n",
                    "title": "Ramadan Economy, 14 nights",
                    "summary": "Makkah and Madinah, shared quad rooms",
                    "kind": "FIXED_DEPARTURE",
                    "tier": "ECONOMY",
                    "totalNights": 14,
                    "makkahNights": 8,
                    "madinahNights": 6,
                    "departureCities": ["LHE"],
                    "currency": "PKR",
                    "featured": true,
                    "heroImageUrl": "https://assets.example.com/packages/ramadan.jpg",
                    "hotels": [
                      {
                        "city": "MAKKAH",
                        "nights": 8,
                        "mealPlan": "BREAKFAST",
                        "name": "Al Safwah",
                        "rating": 4,
                        "distanceToHaramM": 300
                      }
                    ],
                    "fromPrice": {
                      "amount": "198000.00",
                      "currency": "PKR"
                    },
                    "nextDeparture": {
                      "id": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                      "departureDate": "2026-12-20",
                      "returnDate": "2027-01-03",
                      "departureCity": "LHE",
                      "airlineCode": "PK",
                      "availability": {
                        "status": "available"
                      }
                    },
                    "availability": {
                      "status": "available"
                    },
                    "description": "Fourteen nights across Makkah and Madinah.",
                    "inclusions": ["Visa", "Flights"],
                    "exclusions": ["Personal expenses"],
                    "requiredDocuments": ["Passport"],
                    "termsMarkdown": "…",
                    "cancellationPolicyMarkdown": "…",
                    "childMaxAge": 11,
                    "infantMaxAge": 2,
                    "itinerary": [
                      {
                        "dayNo": 1,
                        "title": "Arrive in Jeddah",
                        "body": null
                      }
                    ],
                    "media": [
                      {
                        "url": "https://assets.example.com/packages/ramadan.jpg",
                        "alt": null
                      }
                    ],
                    "prices": [
                      {
                        "priceType": "ADULT_QUAD",
                        "amount": "198000.00",
                        "currency": "PKR"
                      }
                    ],
                    "departures": [
                      {
                        "id": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                        "departureDate": "2026-12-20",
                        "returnDate": "2027-01-03",
                        "departureCity": "LHE",
                        "airlineCode": "PK",
                        "flightSummary": "PK 8xx LHE-JED, PK 8xx MED-LHE",
                        "availability": {
                          "status": "available"
                        },
                        "prices": [
                          {
                            "priceType": "ADULT_QUAD",
                            "amount": "198000.00",
                            "currency": "PKR"
                          },
                          {
                            "priceType": "ADULT_DOUBLE",
                            "amount": "245000.00",
                            "currency": "PKR"
                          }
                        ],
                        "fromPrice": {
                          "amount": "198000.00",
                          "currency": "PKR"
                        }
                      }
                    ]
                  }
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/catalogue/packages/{packageIdOrSlug}/departures": {
      "get": {
        "tags": ["Catalogue"],
        "operationId": "listDepartures",
        "summary": "Departures of a package",
        "description": "Dated departures with a price per room type and an availability hint.",
        "parameters": [
          {
            "name": "packageIdOrSlug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Departure"
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                      "departureDate": "2026-12-20",
                      "returnDate": "2027-01-03",
                      "departureCity": "LHE",
                      "airlineCode": "PK",
                      "flightSummary": "PK 8xx LHE-JED, PK 8xx MED-LHE",
                      "availability": {
                        "status": "available"
                      },
                      "prices": [
                        {
                          "priceType": "ADULT_QUAD",
                          "amount": "198000.00",
                          "currency": "PKR"
                        },
                        {
                          "priceType": "ADULT_DOUBLE",
                          "amount": "245000.00",
                          "currency": "PKR"
                        }
                      ],
                      "fromPrice": {
                        "amount": "198000.00",
                        "currency": "PKR"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/requests": {
      "post": {
        "tags": ["Requests"],
        "operationId": "createRequest",
        "summary": "Create a customer request",
        "description": "One body for both products; `productType` selects which fields apply.\n\n- **Package:** `packageId`, `departureId` (fixed departures), `rooms` (adults and children per room; bed capacity is checked).\n- **Group:** `groupId`, a cabin (`cabinId`, or the stable `cabinCode` Y/W/C/F), and either `pax` or `passengers`.\n\n**Passengers (group requests).** Send `passengers[]` with your own `ref` per traveller, plus name, `type` (ADULT/CHILD/INFANT), and optionally `dob`, `nationality` (ISO alpha-3), `passportNumber` and `passportExpiry`. Seat counts are taken from the list (`pax` may be omitted; if sent it must agree). `ref` must be unique within the request. It is the key ticket numbers come back on: we never match travellers by name. Passport numbers are returned masked.\n\n**Holds.** `hold: true` creates a normal booking in your agency's name (`PAYMENT_PENDING`) and takes the seats immediately. The payment deadline is the group's `paymentDeadlineHours` (`paymentDeadlineAt` / `holdExpiresAt`); callers cannot override it. Needs a secret key with `holds:write` on a hold-enabled integration, and is limited by concurrent-seat and daily-hold caps. Works in both Direct and Connected mode at create time (Connected can also hold on `/forward`). Approve with `POST /requests/{id}/approve` or reject with `/reject` (reject cancels the booking and releases seats). Extend the deadline with `POST /requests/{id}/extension-request` (Airdesk head office must approve). The seats return to sale if you cancel/reject, the deadline passes (`request.expired`), or the booking is cancelled.\n\n**Retries.** `partnerReference` is unique per integration (and per environment). Sending it again, even with a new `Idempotency-Key`, returns `200` with the original request, so a retried push can never double-book.\n\n**Browser keys** must send `turnstileToken` and the call must come from an allowed website. The answer contains only `id`, `reference`, `status`, `createdAt`: contact data is never echoed back to a browser.\n**Secret keys** get the full request. In connected mode the request waits in your inbox (`RECEIVED`) unless you send `forwardImmediately: true`.\nSandbox (test key) requests are created and tracked but never reach head office.\n\nThe same call repeated with the same `Idempotency-Key`, the same `partnerReference`, or the same customer double-clicking within 10 minutes, returns `200` with the original request instead of creating a second one.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRequest"
              },
              "examples": {
                "package": {
                  "summary": "Package request",
                  "value": {
                    "productType": "PACKAGE",
                    "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                    "rooms": [
                      {
                        "type": "QUAD",
                        "adults": 2
                      }
                    ],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": "Prefers a room on a high floor.",
                    "partnerReference": "WEB-2026-00042",
                    "consent": {
                      "accepted": true,
                      "textVersion": "v1"
                    }
                  }
                },
                "group": {
                  "summary": "Group seats",
                  "value": {
                    "productType": "GROUP",
                    "groupId": "4dece828-160a-4782-bea7-1c20ae185c29",
                    "cabinCode": "Y",
                    "passengers": [
                      {
                        "ref": "pax-1",
                        "givenNames": "Sara",
                        "familyName": "Khan",
                        "type": "ADULT",
                        "dob": "1988-04-12",
                        "nationality": "PAK",
                        "passportNumber": "AB1234567",
                        "passportExpiry": "2031-06-30"
                      },
                      {
                        "ref": "pax-2",
                        "givenNames": "Imran",
                        "familyName": "Khan",
                        "type": "ADULT"
                      },
                      {
                        "ref": "pax-3",
                        "givenNames": "Hira",
                        "familyName": "Khan",
                        "type": "CHILD",
                        "dob": "2018-09-01"
                      }
                    ],
                    "hold": true,
                    "partnerReference": "ORDER-88213",
                    "contact": {
                      "name": "Sara Khan",
                      "phone": "+923331234567"
                    },
                    "consent": {
                      "accepted": true
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A replay of an earlier identical call, or a duplicate submission. Same body shape as 201.",
            "headers": {
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": ["true"]
                }
              }
            }
          },
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "anyOf": [
                        {
                          "$ref": "#/components/schemas/Request"
                        },
                        {
                          "$ref": "#/components/schemas/RequestCreated"
                        }
                      ],
                      "description": "Secret keys get the full `Request`; browser keys get only `RequestCreated`."
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "productType": "PACKAGE",
                    "status": "UNDER_REVIEW",
                    "isTest": false,
                    "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                    "groupId": null,
                    "cabinId": null,
                    "pax": {
                      "adults": 2,
                      "children": 0,
                      "infants": 0
                    },
                    "rooms": [
                      {
                        "roomType": "QUAD",
                        "adults": 2,
                        "childrenWithBed": 0,
                        "childrenNoBed": 0,
                        "infants": 0
                      }
                    ],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": null,
                    "amounts": {
                      "currency": "PKR",
                      "net": "360000.00",
                      "retail": "396000.00"
                    },
                    "passengers": [],
                    "holdExpiresAt": null,
                    "fulfilment": null,
                    "decisionReason": null,
                    "createdAt": "2026-10-21T09:15:00.000Z",
                    "updatedAt": "2026-10-21T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Disabled"
          }
        }
      },
      "get": {
        "tags": ["Requests"],
        "operationId": "listRequests",
        "summary": "List your requests",
        "description": "Newest first. `updatedSince` supports incremental sync. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `requests:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/RequestStatus"
            }
          },
          {
            "name": "productType",
            "in": "query",
            "schema": {
              "enum": ["GROUP", "PACKAGE"]
            }
          },
          {
            "name": "updatedSince",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "partnerReference",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": ["data", "nextCursor"],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Request"
                          }
                        },
                        "nextCursor": {
                          "type": ["string", "null"]
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "data": [
                      {
                        "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                        "reference": "PRQ-2026-000070",
                        "partnerReference": "WEB-2026-00042",
                        "productType": "PACKAGE",
                        "status": "UNDER_REVIEW",
                        "isTest": false,
                        "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                        "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                        "groupId": null,
                        "cabinId": null,
                        "pax": {
                          "adults": 2,
                          "children": 0,
                          "infants": 0
                        },
                        "rooms": [
                          {
                            "roomType": "QUAD",
                            "adults": 2,
                            "childrenWithBed": 0,
                            "childrenNoBed": 0,
                            "infants": 0
                          }
                        ],
                        "contact": {
                          "name": "Ali Khan",
                          "phone": "+923001234567",
                          "email": "ali@example.com",
                          "city": "Lahore"
                        },
                        "notes": null,
                        "amounts": {
                          "currency": "PKR",
                          "net": "360000.00",
                          "retail": "396000.00"
                        },
                        "passengers": [],
                        "holdExpiresAt": null,
                        "fulfilment": null,
                        "decisionReason": null,
                        "createdAt": "2026-10-21T09:15:00.000Z",
                        "updatedAt": "2026-10-21T09:15:00.000Z"
                      }
                    ],
                    "nextCursor": null
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/requests/{id}": {
      "get": {
        "tags": ["Requests"],
        "operationId": "getRequest",
        "summary": "One request",
        "description": "Look up by id, or by your own reference with `ref:<partnerReference>`. Once head office has booked a group request, `fulfilment` carries the booking reference, every PNR, and each passenger's PNR and ticket number keyed by your passenger `ref` (null values until issued). A `request.fulfilled` event fires when the booking is ticketed, whoever ticketed it. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "ref:WEB-2026-00042"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Request"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "productType": "GROUP",
                    "status": "FULFILLED",
                    "isTest": false,
                    "packageId": null,
                    "departureId": null,
                    "groupId": "4dece828-160a-4782-bea7-1c20ae185c29",
                    "cabinId": "0f6c2d1e-6a3b-4f6e-9b1a-2c7d9e4a1b10",
                    "pax": {
                      "adults": 2,
                      "children": 0,
                      "infants": 0
                    },
                    "rooms": [],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": null,
                    "amounts": {
                      "currency": "PKR",
                      "net": "360000.00",
                      "retail": "396000.00"
                    },
                    "passengers": [
                      {
                        "ref": "pax-1",
                        "givenNames": "Sara",
                        "familyName": "Khan",
                        "type": "ADULT",
                        "dob": "1988-04-12",
                        "nationality": "PAK",
                        "passportNumber": "•••••••567",
                        "passportExpiry": "2031-06-30"
                      },
                      {
                        "ref": "pax-2",
                        "givenNames": "Imran",
                        "familyName": "Khan",
                        "type": "ADULT",
                        "dob": null,
                        "nationality": null,
                        "passportNumber": null,
                        "passportExpiry": null
                      }
                    ],
                    "holdExpiresAt": null,
                    "fulfilment": {
                      "bookingReference": "BKG-2026-004412",
                      "pnrs": ["X7K2QP", "X7K2QR"],
                      "passengers": [
                        {
                          "ref": "pax-1",
                          "pnr": "X7K2QP",
                          "ticketNumber": "1762400000011"
                        },
                        {
                          "ref": "pax-2",
                          "pnr": "X7K2QR",
                          "ticketNumber": "1762400000022"
                        }
                      ]
                    },
                    "decisionReason": null,
                    "createdAt": "2026-10-21T09:15:00.000Z",
                    "updatedAt": "2026-10-21T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/requests/{id}/cancel": {
      "post": {
        "tags": ["Requests"],
        "operationId": "cancelRequest",
        "summary": "Cancel a request",
        "description": "Before head office confirms, the request is cancelled at once. After confirmation a cancellation request is raised (`CANCELLATION_REQUESTED`) and head office decides. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `requests:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Reason"
              },
              "example": {
                "reason": "Customer cannot travel on these dates."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Request"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "productType": "PACKAGE",
                    "status": "CANCELLED",
                    "isTest": false,
                    "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                    "groupId": null,
                    "cabinId": null,
                    "pax": {
                      "adults": 2,
                      "children": 0,
                      "infants": 0
                    },
                    "rooms": [
                      {
                        "roomType": "QUAD",
                        "adults": 2,
                        "childrenWithBed": 0,
                        "childrenNoBed": 0,
                        "infants": 0
                      }
                    ],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": null,
                    "amounts": {
                      "currency": "PKR",
                      "net": "360000.00",
                      "retail": "396000.00"
                    },
                    "passengers": [],
                    "holdExpiresAt": null,
                    "fulfilment": null,
                    "decisionReason": "Customer cannot travel on these dates.",
                    "createdAt": "2026-10-21T09:15:00.000Z",
                    "updatedAt": "2026-10-21T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/requests/{id}/forward": {
      "post": {
        "tags": ["Requests"],
        "operationId": "forwardRequest",
        "summary": "Forward an inbox request to head office",
        "description": "Connected mode only: `RECEIVED` becomes `UNDER_REVIEW`. Send `rooms` (package) or `pax` (group) to change what is forwarded; the request is re-priced. Send `hold: true` (group, needs `holds:write`) to take the seats as it is forwarded; the request becomes `HELD`. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `requests:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForwardRequest"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Request"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "productType": "PACKAGE",
                    "status": "UNDER_REVIEW",
                    "isTest": false,
                    "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                    "groupId": null,
                    "cabinId": null,
                    "pax": {
                      "adults": 2,
                      "children": 0,
                      "infants": 0
                    },
                    "rooms": [
                      {
                        "roomType": "QUAD",
                        "adults": 2,
                        "childrenWithBed": 0,
                        "childrenNoBed": 0,
                        "infants": 0
                      }
                    ],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": null,
                    "amounts": {
                      "currency": "PKR",
                      "net": "360000.00",
                      "retail": "396000.00"
                    },
                    "passengers": [],
                    "holdExpiresAt": null,
                    "fulfilment": null,
                    "decisionReason": null,
                    "createdAt": "2026-10-21T09:15:00.000Z",
                    "updatedAt": "2026-10-21T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/requests/{id}/approve": {
      "post": {
        "tags": ["Requests"],
        "operationId": "approveHeldRequest",
        "summary": "Approve a held partner booking",
        "description": "Approve a held group booking created with `hold: true`. The booking stays `PAYMENT_PENDING` in your agency tenant; pay or confirm it the usual way. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `requests:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Request"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "productType": "PACKAGE",
                    "status": "HELD",
                    "isTest": false,
                    "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                    "groupId": null,
                    "cabinId": null,
                    "pax": {
                      "adults": 2,
                      "children": 0,
                      "infants": 0
                    },
                    "rooms": [
                      {
                        "roomType": "QUAD",
                        "adults": 2,
                        "childrenWithBed": 0,
                        "childrenNoBed": 0,
                        "infants": 0
                      }
                    ],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": null,
                    "amounts": {
                      "currency": "PKR",
                      "net": "360000.00",
                      "retail": "396000.00"
                    },
                    "passengers": [],
                    "holdExpiresAt": null,
                    "fulfilment": null,
                    "decisionReason": null,
                    "createdAt": "2026-10-21T09:15:00.000Z",
                    "updatedAt": "2026-10-21T09:15:00.000Z",
                    "agentApproval": "APPROVED"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/requests/{id}/reject": {
      "post": {
        "tags": ["Requests"],
        "operationId": "rejectRequest",
        "summary": "Decline an inbox request or reject a held booking",
        "description": "Connected inbox: `RECEIVED` becomes `AGENT_REJECTED` (head office never sees it). Held partner bookings awaiting approval: cancels the booking, releases seats, and sets `AGENT_REJECTED`. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `requests:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Reason"
              },
              "example": {
                "reason": "Customer unreachable."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Request"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "productType": "PACKAGE",
                    "status": "AGENT_REJECTED",
                    "isTest": false,
                    "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                    "groupId": null,
                    "cabinId": null,
                    "pax": {
                      "adults": 2,
                      "children": 0,
                      "infants": 0
                    },
                    "rooms": [
                      {
                        "roomType": "QUAD",
                        "adults": 2,
                        "childrenWithBed": 0,
                        "childrenNoBed": 0,
                        "infants": 0
                      }
                    ],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": null,
                    "amounts": {
                      "currency": "PKR",
                      "net": "360000.00",
                      "retail": "396000.00"
                    },
                    "passengers": [],
                    "holdExpiresAt": null,
                    "fulfilment": null,
                    "decisionReason": "Customer unreachable.",
                    "createdAt": "2026-10-21T09:15:00.000Z",
                    "updatedAt": "2026-10-21T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/requests/{id}/extension-request": {
      "post": {
        "tags": ["Requests"],
        "operationId": "requestDeadlineExtension",
        "summary": "Ask Airdesk to extend the payment deadline",
        "description": "For a held partner booking in your agency's name. Head office must approve; on approval `holdExpiresAt` / `paymentDeadlineAt` move and `request.deadline_extended` is emitted. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `requests:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["extensionMinutes"],
                "properties": {
                  "extensionMinutes": {
                    "type": "integer",
                    "minimum": 15,
                    "maximum": 10080
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              },
              "example": {
                "extensionMinutes": 60,
                "reason": "Customer payment delayed"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Request"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "productType": "PACKAGE",
                    "status": "UNDER_REVIEW",
                    "isTest": false,
                    "packageId": "9f87e56a-0d08-4f58-93b1-83c0352d6095",
                    "departureId": "3d7744e0-c7a4-489f-b556-c3a9119e60a5",
                    "groupId": null,
                    "cabinId": null,
                    "pax": {
                      "adults": 2,
                      "children": 0,
                      "infants": 0
                    },
                    "rooms": [
                      {
                        "roomType": "QUAD",
                        "adults": 2,
                        "childrenWithBed": 0,
                        "childrenNoBed": 0,
                        "infants": 0
                      }
                    ],
                    "contact": {
                      "name": "Ali Khan",
                      "phone": "+923001234567",
                      "email": "ali@example.com",
                      "city": "Lahore"
                    },
                    "notes": null,
                    "amounts": {
                      "currency": "PKR",
                      "net": "360000.00",
                      "retail": "396000.00"
                    },
                    "passengers": [],
                    "holdExpiresAt": null,
                    "fulfilment": null,
                    "decisionReason": null,
                    "createdAt": "2026-10-21T09:15:00.000Z",
                    "updatedAt": "2026-10-21T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/events": {
      "get": {
        "tags": ["Events"],
        "operationId": "listEvents",
        "summary": "Event feed",
        "description": "Everything that happened to your requests, in order. This is the polling alternative to webhooks and the way to recover anything a webhook missed.\n\nPass the highest `sequence` you have processed as `after`. Events younger than three seconds are held back so a slow transaction cannot appear behind your cursor; re-reading a few events is harmless because every event has a unique `id`.\nNeeds a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `events:read`.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Return events with a higher sequence number."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": ["data", "nextAfter"],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Event"
                          }
                        },
                        "nextAfter": {
                          "type": ["integer", "null"],
                          "description": "The last sequence in this page, or null when the page is empty."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "data": [
                      {
                        "id": "evt_6a3c1e52-2f4d-4c0e-9d5b-1f0b7a9d3c11",
                        "sequence": 1042,
                        "type": "request.confirmed",
                        "apiVersion": "2026-10-01",
                        "createdAt": "2026-10-21T11:02:10.000Z",
                        "livemode": true,
                        "data": {
                          "request": {
                            "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                            "reference": "PRQ-2026-000070",
                            "partnerReference": "WEB-2026-00042",
                            "status": "CONFIRMED",
                            "updatedAt": "2026-10-21T11:02:09.000Z"
                          }
                        }
                      }
                    ],
                    "nextAfter": 1042
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/webhook-endpoints": {
      "get": {
        "tags": ["Events"],
        "operationId": "listWebhookEndpoints",
        "summary": "Your webhook endpoints",
        "description": "Read only: endpoints are created and changed in the portal. Secrets are never returned. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `events:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "7b0f6d2c-5d2a-4a76-8a5e-0c5b1a1c9e22",
                      "url": "https://example.com/airdesk/webhook",
                      "eventTypes": [],
                      "enabled": true,
                      "disabledReason": null,
                      "rotatingUntil": null,
                      "createdAt": "2026-10-20T08:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/partner/webhook-endpoints/{id}/test": {
      "post": {
        "tags": ["Events"],
        "operationId": "testWebhookEndpoint",
        "summary": "Send a test event",
        "description": "Sends a `webhook.test` event to one endpoint and reports what your receiver answered. Needs a **secret key**; browser keys get `403 partner_api.scope_missing`. Needs scope `events:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["success", "data"],
                  "properties": {
                    "success": {
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/WebhookTestResult"
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "ok": true,
                    "statusCode": 200,
                    "error": null,
                    "status": "SUCCEEDED"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "webhooks": {
    "requestStatusChanged": {
      "post": {
        "tags": ["Events"],
        "summary": "A request changed (webhook delivery)",
        "description": "Airdesk calls the https URL you registered in the portal. Events are **thin**: they carry identifiers and the new status, never contact details. Fetch the full record with `GET /v1/partner/requests/{id}`.\n\n### Headers\n- `Airdesk-Webhook-Id`: the event id (`evt_…`). Deduplicate on it; the same event can be delivered more than once.\n- `Airdesk-Webhook-Timestamp`: Unix seconds.\n- `Airdesk-Webhook-Signature`: `v1=<hex>`, the HMAC-SHA256 of `\"<id>.<timestamp>.<raw body>\"` with your endpoint's signing secret (`adk_whsec_…`). While a secret is being rotated there are two comma-separated `v1=` values; accept either.\n\n### Verify, then answer fast\n1. Read the **raw** body (do not re-serialise the JSON).\n2. Recompute the HMAC and compare it to each `v1=` value in **constant time**.\n3. Reject timestamps more than five minutes from now (replay protection).\n4. Deduplicate on the event id, then answer `2xx` quickly and do the work afterwards.\n\nComplete examples for Node, PHP and Python are in the webhook guide.\n\n### Delivery and retries\nAny answer other than `2xx` (or no answer within 10 seconds) is a failure. Retries: 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours, then daily, for up to 72 hours. An endpoint that fails continuously for three days is switched off and your notify addresses are emailed. Deliveries from the last 30 days can be resent from the portal. Redirects are not followed. Order is not guaranteed: use `updatedAt` and fetch the request.\n\n### Event types\n`request.received`, `request.forwarded`, `request.under_review`, `request.held`, `request.agent_approved`, `request.agent_rejected`, `request.deadline_extended`, `request.confirmed`, `request.rejected`, `request.cancellation_requested`, `request.cancelled`, `request.expired`, `request.fulfilled`, `integration.suspended`, and `webhook.test`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Event"
              },
              "example": {
                "id": "evt_6a3c1e52-2f4d-4c0e-9d5b-1f0b7a9d3c11",
                "sequence": 1042,
                "type": "request.confirmed",
                "apiVersion": "2026-10-01",
                "createdAt": "2026-10-21T11:02:10.000Z",
                "livemode": true,
                "data": {
                  "request": {
                    "id": "fc7863b8-9f51-48f3-97cb-40666e81b601",
                    "reference": "PRQ-2026-000070",
                    "partnerReference": "WEB-2026-00042",
                    "status": "CONFIRMED",
                    "updatedAt": "2026-10-21T11:02:09.000Z"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Return any 2xx to acknowledge."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "adk_sk_… (secret) or adk_pk_… (browser)",
        "description": "Your API key as a bearer token."
      },
      "airdeskKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Airdesk-Key",
        "description": "Alternative to the Authorization header for browser keys. Never put a key in the URL."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "pattern": "^[\\x21-\\x7e]+$"
        },
        "description": "A unique value you generate per logical operation (a UUID works). Reuse it to retry safely.",
        "example": "web-2026-00042-create"
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "schema": {
          "type": "string",
          "maxLength": 200
        },
        "description": "Opaque cursor from the previous page's `nextCursor`."
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50,
          "default": 20
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, invalid, revoked or expired key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "error": {
                "code": "partner_api.invalid_key",
                "message": "Missing or invalid API key.",
                "requestId": "0b7f0a52-65c5-4f44-bd43-0c6d4f0b3d11"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key may not do this (wrong scope or key type, origin or IP not allowed, integration not active).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "error": {
                "code": "partner_api.scope_missing",
                "message": "This key is missing a required scope.",
                "requestId": "0b7f0a52-65c5-4f44-bd43-0c6d4f0b3d11",
                "details": {
                  "missingScopes": ["requests:read"]
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "No such resource for this integration.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "error": {
                "code": "partner_api.not_found",
                "message": "Request not found.",
                "requestId": "0b7f0a52-65c5-4f44-bd43-0c6d4f0b3d11"
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "Idempotency conflict, not enough availability, a hold limit, or a state that does not allow this action.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "error": {
                "code": "partner_api.insufficient_availability",
                "message": "Not enough seats are left in this cabin for that many travellers.",
                "requestId": "0b7f0a52-65c5-4f44-bd43-0c6d4f0b3d11"
              }
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "The request is not valid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "error": {
                "code": "partner_api.validation_failed",
                "message": "The request is not valid.",
                "requestId": "0b7f0a52-65c5-4f44-bd43-0c6d4f0b3d11",
                "details": {
                  "fields": ["contact.phone must be longer than or equal to 5 characters"]
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit or daily quota exceeded. See Retry-After.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "error": {
                "code": "partner_api.rate_limited",
                "message": "Too many requests. Slow down and retry shortly.",
                "requestId": "0b7f0a52-65c5-4f44-bd43-0c6d4f0b3d11"
              }
            }
          }
        },
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait.",
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "Disabled": {
        "description": "The partner API, or this capability, is not available on this deployment.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "success": false,
              "error": {
                "code": "partner_api.disabled",
                "message": "The partner API is not available.",
                "requestId": "0b7f0a52-65c5-4f44-bd43-0c6d4f0b3d11"
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["success", "error"],
        "properties": {
          "success": {
            "const": false
          },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable, `partner_api.*`. See the error catalogue.",
                "enum": [
                  "partner_api.invalid_key",
                  "partner_api.key_revoked",
                  "partner_api.origin_not_allowed",
                  "partner_api.ip_not_allowed",
                  "partner_api.scope_missing",
                  "partner_api.integration_suspended",
                  "partner_api.not_found",
                  "partner_api.duplicate_reference",
                  "partner_api.holds_disabled",
                  "partner_api.hold_cap_exceeded",
                  "partner_api.idempotency_conflict",
                  "partner_api.idempotency_in_progress",
                  "partner_api.invalid_state_transition",
                  "partner_api.insufficient_availability",
                  "partner_api.validation_failed",
                  "partner_api.turnstile_failed",
                  "partner_api.idempotency_key_required",
                  "partner_api.rate_limited",
                  "partner_api.quota_exceeded",
                  "partner_api.disabled"
                ]
              },
              "message": {
                "type": "string",
                "description": "For humans. Do not parse it."
              },
              "requestId": {
                "type": "string",
                "description": "Quote this in support requests."
              },
              "details": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "Me": {
        "type": "object",
        "required": ["integration", "key", "scopes", "apiVersion"],
        "properties": {
          "integration": {
            "type": "object",
            "required": ["id", "name", "mode"],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "mode": {
                "enum": ["DIRECT", "CONNECTED"]
              }
            }
          },
          "key": {
            "type": "object",
            "required": ["id", "type", "environment"],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "type": {
                "enum": ["publishable", "secret"]
              },
              "environment": {
                "enum": ["live", "test"]
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "enum": [
                "catalogue:read",
                "requests:create",
                "requests:read",
                "requests:manage",
                "holds:write",
                "events:read"
              ]
            }
          },
          "apiVersion": {
            "type": "string"
          },
          "rateLimits": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "Price": {
        "type": "object",
        "required": ["amount", "currency"],
        "properties": {
          "amount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "What the customer pays, per person: your markup applied."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "netAmount": {
            "type": "string",
            "pattern": "^\\d+\\.\\d{2}$",
            "description": "What you are billed. **Secret keys only**; absent for browser keys."
          }
        },
        "examples": [
          {
            "amount": "198000.00",
            "currency": "PKR"
          },
          {
            "amount": "198000.00",
            "currency": "PKR",
            "netAmount": "180000.00"
          }
        ]
      },
      "Availability": {
        "type": "object",
        "required": ["status"],
        "properties": {
          "status": {
            "enum": ["available", "limited", "sold_out"],
            "description": "A hint. The authoritative check happens when you create the request (`409 partner_api.insufficient_availability`)."
          },
          "seats": {
            "type": "integer",
            "description": "Exact seats. Secret keys only, and only when the group allows showing them."
          }
        }
      },
      "Group": {
        "type": "object",
        "required": [
          "id",
          "code",
          "name",
          "airline",
          "tripPattern",
          "currency",
          "availability",
          "segments",
          "cabins"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "airline": {
            "type": "object",
            "required": ["code", "name"],
            "properties": {
              "code": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "logoUrl": {
                "type": ["string", "null"]
              }
            }
          },
          "tripPattern": {
            "enum": ["ONE_WAY", "ROUND_TRIP"]
          },
          "currency": {
            "type": "string"
          },
          "refundable": {
            "type": "boolean"
          },
          "paymentDeadlineHours": {
            "type": "integer"
          },
          "availability": {
            "$ref": "#/components/schemas/Availability"
          },
          "segments": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "order",
                "flightNumber",
                "origin",
                "destination",
                "departureAt",
                "arrivalAt"
              ],
              "properties": {
                "order": {
                  "type": "integer"
                },
                "direction": {
                  "enum": ["OUTBOUND", "INBOUND", "SINGLE", null]
                },
                "flightNumber": {
                  "type": "string"
                },
                "airlineCode": {
                  "type": "string"
                },
                "origin": {
                  "type": "string"
                },
                "destination": {
                  "type": "string"
                },
                "departureAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "arrivalAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "aircraftType": {
                  "type": ["string", "null"]
                },
                "baggage": {
                  "type": ["object", "null"],
                  "additionalProperties": true,
                  "description": "Customer-facing baggage facts, for example `weightKg` and `services`."
                }
              }
            }
          },
          "cabins": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["id", "code", "name", "prices", "availability"],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Use as `cabinId` when creating a group request."
                },
                "code": {
                  "enum": ["Y", "W", "C", "F"]
                },
                "name": {
                  "type": "string"
                },
                "prices": {
                  "type": "object",
                  "required": ["adult"],
                  "properties": {
                    "adult": {
                      "$ref": "#/components/schemas/Price"
                    },
                    "child": {
                      "$ref": "#/components/schemas/Price"
                    },
                    "infant": {
                      "$ref": "#/components/schemas/Price"
                    }
                  },
                  "description": "Per passenger type. `child` / `infant` are present only when a fare exists."
                },
                "availability": {
                  "$ref": "#/components/schemas/Availability"
                }
              }
            }
          }
        }
      },
      "PackageListItem": {
        "type": "object",
        "required": ["id", "slug", "title", "kind", "tier", "currency"],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "type": ["string", "null"]
          },
          "kind": {
            "enum": ["FIXED_DEPARTURE", "FLEXIBLE"]
          },
          "tier": {
            "enum": ["ECONOMY", "STANDARD", "PREMIUM", "LUXURY"]
          },
          "totalNights": {
            "type": ["integer", "null"]
          },
          "makkahNights": {
            "type": "integer"
          },
          "madinahNights": {
            "type": "integer"
          },
          "departureCities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "currency": {
            "type": "string"
          },
          "featured": {
            "type": "boolean"
          },
          "heroImageUrl": {
            "type": ["string", "null"]
          },
          "hotels": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "fromPrice": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Price"
              },
              {
                "type": "null"
              }
            ],
            "description": "Lowest per-person price, with your markup."
          },
          "nextDeparture": {
            "type": ["object", "null"],
            "additionalProperties": true
          },
          "availability": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Availability"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "PackageDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PackageListItem"
          },
          {
            "type": "object",
            "properties": {
              "description": {
                "type": ["string", "null"]
              },
              "itinerary": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "dayNo": {
                      "type": "integer"
                    },
                    "title": {
                      "type": ["string", "null"]
                    },
                    "body": {
                      "type": ["string", "null"]
                    }
                  }
                }
              },
              "media": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": ["string", "null"]
                    },
                    "alt": {
                      "type": ["string", "null"]
                    }
                  }
                }
              },
              "inclusions": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "exclusions": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "requiredDocuments": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "termsMarkdown": {
                "type": ["string", "null"],
                "description": "Markdown. Render it as text or sanitise it."
              },
              "cancellationPolicyMarkdown": {
                "type": ["string", "null"]
              },
              "childMaxAge": {
                "type": "integer"
              },
              "infantMaxAge": {
                "type": "integer"
              },
              "prices": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": ["priceType", "amount", "currency"],
                  "properties": {
                    "priceType": {
                      "type": "string",
                      "examples": ["ADULT_QUAD"]
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "netAmount": {
                      "type": "string"
                    }
                  }
                }
              },
              "departures": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Departure"
                }
              }
            }
          }
        ]
      },
      "Departure": {
        "type": "object",
        "required": [
          "id",
          "departureDate",
          "returnDate",
          "departureCity",
          "availability",
          "prices"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Use as `departureId` when creating a package request."
          },
          "departureDate": {
            "type": "string",
            "format": "date"
          },
          "returnDate": {
            "type": "string",
            "format": "date"
          },
          "departureCity": {
            "type": "string"
          },
          "airlineCode": {
            "type": ["string", "null"]
          },
          "flightSummary": {
            "type": ["string", "null"]
          },
          "availability": {
            "$ref": "#/components/schemas/Availability"
          },
          "prices": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["priceType", "amount", "currency"],
              "properties": {
                "priceType": {
                  "type": "string"
                },
                "amount": {
                  "type": "string"
                },
                "currency": {
                  "type": "string"
                },
                "netAmount": {
                  "type": "string"
                }
              }
            }
          },
          "fromPrice": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Price"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "RequestStatus": {
        "enum": [
          "RECEIVED",
          "AGENT_REJECTED",
          "FORWARDED",
          "UNDER_REVIEW",
          "HELD",
          "CONFIRMED",
          "CANCELLATION_REQUESTED",
          "CANCELLED",
          "REJECTED",
          "EXPIRED",
          "FULFILLED"
        ],
        "description": "`RECEIVED` and `AGENT_REJECTED` exist only in connected mode. After head office takes over, the status follows its record and cannot drift."
      },
      "CreateRequest": {
        "type": "object",
        "required": ["productType", "contact", "consent"],
        "additionalProperties": false,
        "properties": {
          "productType": {
            "enum": ["GROUP", "PACKAGE"]
          },
          "packageId": {
            "type": "string",
            "format": "uuid"
          },
          "departureId": {
            "type": "string",
            "format": "uuid",
            "description": "Required for fixed-departure packages."
          },
          "preferredDates": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "from": {
                "type": "string",
                "format": "date"
              },
              "to": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "rooms": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/Room"
            },
            "description": "Packages: one entry per room."
          },
          "groupId": {
            "type": "string",
            "format": "uuid"
          },
          "cabinId": {
            "type": "string",
            "description": "A `cabins[].id` from the group. Send this or `cabinCode`."
          },
          "cabinCode": {
            "enum": ["Y", "W", "C", "F"],
            "description": "The cabin's own code. Stable when the group is edited or republished; use it as your key."
          },
          "pax": {
            "$ref": "#/components/schemas/Pax"
          },
          "passengers": {
            "type": "array",
            "maxItems": 30,
            "items": {
              "$ref": "#/components/schemas/PassengerInput"
            },
            "description": "Group requests: the named travellers. Defines the seat counts."
          },
          "hold": {
            "type": "boolean",
            "description": "Group requests, secret keys with `holds:write`: take the seats now."
          },
          "contact": {
            "type": "object",
            "required": ["name", "phone"],
            "additionalProperties": false,
            "properties": {
              "name": {
                "type": "string",
                "minLength": 2,
                "maxLength": 120
              },
              "phone": {
                "type": "string",
                "description": "International format, e.g. +923001234567."
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "city": {
                "type": "string",
                "maxLength": 80
              }
            }
          },
          "notes": {
            "type": "string",
            "maxLength": 1000,
            "description": "Plain text. Control and direction-override characters are removed."
          },
          "partnerReference": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._\\-/]{1,64}$",
            "description": "Your own reference. Unique per integration."
          },
          "consent": {
            "type": "object",
            "required": ["accepted"],
            "additionalProperties": false,
            "properties": {
              "accepted": {
                "const": true,
                "description": "The customer agreed to be contacted."
              },
              "textVersion": {
                "type": "string",
                "maxLength": 32
              }
            }
          },
          "turnstileToken": {
            "type": "string",
            "maxLength": 2048,
            "description": "Browser keys only: the Cloudflare Turnstile token from your page."
          },
          "forwardImmediately": {
            "type": "boolean",
            "description": "Connected mode, secret keys only: skip the inbox."
          }
        }
      },
      "PassengerInput": {
        "type": "object",
        "required": ["ref", "givenNames", "familyName", "type"],
        "additionalProperties": false,
        "properties": {
          "ref": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._\\-/]{1,64}$",
            "description": "Your own key for this traveller, unique within the request. Ticket numbers are returned against it."
          },
          "givenNames": {
            "type": "string",
            "maxLength": 60
          },
          "familyName": {
            "type": "string",
            "maxLength": 60
          },
          "type": {
            "enum": ["ADULT", "CHILD", "INFANT"]
          },
          "dob": {
            "type": "string",
            "format": "date"
          },
          "nationality": {
            "type": "string",
            "pattern": "^[A-Za-z]{3}$",
            "description": "ISO 3166-1 alpha-3."
          },
          "passportNumber": {
            "type": "string",
            "pattern": "^[A-Za-z0-9]{5,20}$"
          },
          "passportExpiry": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "Passenger": {
        "type": "object",
        "properties": {
          "ref": {
            "type": "string"
          },
          "givenNames": {
            "type": "string"
          },
          "familyName": {
            "type": "string"
          },
          "type": {
            "enum": ["ADULT", "CHILD", "INFANT"]
          },
          "dob": {
            "type": ["string", "null"],
            "format": "date"
          },
          "nationality": {
            "type": ["string", "null"]
          },
          "passportNumber": {
            "type": ["string", "null"],
            "description": "Masked: only the last three characters are shown."
          },
          "passportExpiry": {
            "type": ["string", "null"],
            "format": "date"
          }
        }
      },
      "Fulfilment": {
        "type": ["object", "null"],
        "description": "Group requests once head office has booked them; null before that and after the request is closed without a booking.",
        "required": ["bookingReference", "pnrs", "passengers"],
        "properties": {
          "bookingReference": {
            "type": "string"
          },
          "pnrs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Every airline PNR on the booking (multi-PNR bookings exist)."
          },
          "passengers": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["ref", "pnr", "ticketNumber"],
              "properties": {
                "ref": {
                  "type": "string",
                  "description": "Your passenger ref."
                },
                "pnr": {
                  "type": ["string", "null"]
                },
                "ticketNumber": {
                  "type": ["string", "null"],
                  "description": "Null until the ticket is issued."
                }
              }
            }
          }
        }
      },
      "Room": {
        "type": "object",
        "required": ["type", "adults"],
        "additionalProperties": false,
        "properties": {
          "type": {
            "enum": ["SHARING", "QUAD", "TRIPLE", "DOUBLE", "SINGLE"]
          },
          "adults": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10
          },
          "childrenWithBed": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10
          },
          "childrenNoBed": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10
          },
          "infants": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "description": "At most one per adult."
          }
        },
        "description": "Adults plus children with a bed must fit the room: SINGLE 1, DOUBLE 2, TRIPLE 3, QUAD and SHARING 4."
      },
      "Pax": {
        "type": "object",
        "required": ["adults", "children", "infants"],
        "additionalProperties": false,
        "properties": {
          "adults": {
            "type": "integer",
            "minimum": 0,
            "maximum": 30
          },
          "children": {
            "type": "integer",
            "minimum": 0,
            "maximum": 30
          },
          "infants": {
            "type": "integer",
            "minimum": 0,
            "maximum": 30
          }
        },
        "description": "At most 30 travellers (adults plus children) per request, and no more infants than adults."
      },
      "Request": {
        "type": "object",
        "required": [
          "id",
          "reference",
          "productType",
          "status",
          "contact",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string",
            "examples": ["PRQ-2026-000070"]
          },
          "partnerReference": {
            "type": ["string", "null"]
          },
          "productType": {
            "enum": ["GROUP", "PACKAGE"]
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "isTest": {
            "type": "boolean"
          },
          "packageId": {
            "type": ["string", "null"]
          },
          "departureId": {
            "type": ["string", "null"]
          },
          "groupId": {
            "type": ["string", "null"]
          },
          "cabinId": {
            "type": ["string", "null"]
          },
          "pax": {
            "$ref": "#/components/schemas/Pax"
          },
          "rooms": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "contact": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "phone": {
                "type": "string"
              },
              "email": {
                "type": ["string", "null"]
              },
              "city": {
                "type": ["string", "null"]
              }
            }
          },
          "notes": {
            "type": ["string", "null"]
          },
          "amounts": {
            "type": ["object", "null"],
            "properties": {
              "currency": {
                "type": "string"
              },
              "net": {
                "type": "string",
                "description": "What you are billed."
              },
              "retail": {
                "type": "string",
                "description": "What the customer pays, with your markup."
              }
            },
            "description": "A snapshot at the time of the request."
          },
          "passengers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Passenger"
            }
          },
          "holdExpiresAt": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "When the held seats go back on sale unless the booking is paid or extended."
          },
          "paymentDeadlineAt": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "Airdesk payment deadline from the group's paymentDeadlineHours (same as holdExpiresAt for held partner bookings)."
          },
          "bookingReference": {
            "type": ["string", "null"],
            "description": "Reference of the agency booking created for a held group request."
          },
          "bookingId": {
            "type": ["string", "null"],
            "format": "uuid"
          },
          "agentApproval": {
            "type": ["string", "null"],
            "enum": ["PENDING", "APPROVED", "REJECTED", null],
            "description": "Agency approval state for held partner bookings."
          },
          "fulfilment": {
            "$ref": "#/components/schemas/Fulfilment"
          },
          "decisionReason": {
            "type": ["string", "null"]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RequestCreated": {
        "type": "object",
        "required": ["id", "reference", "status", "createdAt"],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "description": "What a browser key receives: enough to say \"we got it\", and nothing about the customer."
      },
      "ForwardRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "rooms": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/Room"
            }
          },
          "pax": {
            "$ref": "#/components/schemas/Pax"
          },
          "hold": {
            "type": "boolean",
            "description": "Group requests, needs `holds:write`: take the seats as the request is forwarded."
          }
        }
      },
      "Reason": {
        "type": "object",
        "required": ["reason"],
        "additionalProperties": false,
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 2,
            "maxLength": 300
          }
        }
      },
      "Event": {
        "type": "object",
        "required": ["id", "type", "apiVersion", "createdAt", "livemode", "data"],
        "properties": {
          "id": {
            "type": "string",
            "description": "`evt_…`: unique and stable. Deduplicate on it."
          },
          "sequence": {
            "type": "integer",
            "description": "Present in the /events feed; not in webhook bodies."
          },
          "type": {
            "type": "string",
            "examples": ["request.confirmed"]
          },
          "apiVersion": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "livemode": {
            "type": "boolean",
            "description": "False for sandbox (test key) events."
          },
          "data": {
            "type": "object",
            "description": "Identifiers and status only. `request` for request events, `integration` for integration events, free text for `webhook.test`.",
            "additionalProperties": true,
            "properties": {
              "request": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "partnerReference": {
                    "type": ["string", "null"]
                  },
                  "status": {
                    "$ref": "#/components/schemas/RequestStatus"
                  },
                  "updatedAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": ["id", "url", "enabled"],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "eventTypes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Empty means every event."
          },
          "enabled": {
            "type": "boolean"
          },
          "disabledReason": {
            "type": ["string", "null"]
          },
          "rotatingUntil": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "While set, two signatures are sent."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookTestResult": {
        "type": "object",
        "required": ["ok", "status"],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "statusCode": {
            "type": ["integer", "null"]
          },
          "error": {
            "type": ["string", "null"]
          },
          "status": {
            "enum": ["SUCCEEDED", "FAILED", "DEAD"]
          }
        }
      }
    }
  },
  "x-error-catalogue": [
    {
      "code": "partner_api.invalid_key",
      "status": 401,
      "meaning": "The key is missing, malformed, unknown or wrong. These cases are deliberately indistinguishable. Keys must be sent in the Authorization header, never in the URL."
    },
    {
      "code": "partner_api.key_revoked",
      "status": 401,
      "meaning": "The key was revoked or has expired."
    },
    {
      "code": "partner_api.origin_not_allowed",
      "status": 403,
      "meaning": "A browser key was used from a website that is not on the integration's allowed list, or a secret key was used from a browser."
    },
    {
      "code": "partner_api.ip_not_allowed",
      "status": 403,
      "meaning": "A secret key was used from an IP address outside the integration's allowlist."
    },
    {
      "code": "partner_api.scope_missing",
      "status": 403,
      "meaning": "The key does not carry the scope this endpoint needs, or is the wrong type (for example a browser key on a secret-key endpoint)."
    },
    {
      "code": "partner_api.integration_suspended",
      "status": 403,
      "meaning": "The integration, agency or workspace is not active."
    },
    {
      "code": "partner_api.not_found",
      "status": 404,
      "meaning": "No such resource for this integration. Also returned for resources that belong to someone else, so ids cannot be probed."
    },
    {
      "code": "partner_api.duplicate_reference",
      "status": 409,
      "meaning": "Reserved. Repeating a partnerReference returns the original request with 200 instead of an error."
    },
    {
      "code": "partner_api.holds_disabled",
      "status": 403,
      "meaning": "Seat holds are not enabled for this integration. Ask head office to enable them."
    },
    {
      "code": "partner_api.hold_cap_exceeded",
      "status": 409,
      "meaning": "The integration's concurrent held seats or daily holds limit would be exceeded."
    },
    {
      "code": "partner_api.idempotency_conflict",
      "status": 409,
      "meaning": "This Idempotency-Key was already used with a different request."
    },
    {
      "code": "partner_api.idempotency_in_progress",
      "status": 409,
      "meaning": "The first request with this Idempotency-Key is still running. Retry shortly."
    },
    {
      "code": "partner_api.invalid_state_transition",
      "status": 409,
      "meaning": "The request is not in a state that allows this action."
    },
    {
      "code": "partner_api.insufficient_availability",
      "status": 409,
      "meaning": "Not enough seats are left. Availability is a hint; this is the authoritative check."
    },
    {
      "code": "partner_api.validation_failed",
      "status": 422,
      "meaning": "The request is not valid. `details.fields` lists the problems when they are field-level."
    },
    {
      "code": "partner_api.turnstile_failed",
      "status": 422,
      "meaning": "The human-verification check failed. Reload the page and try again."
    },
    {
      "code": "partner_api.idempotency_key_required",
      "status": 422,
      "meaning": "Writes need an Idempotency-Key header (1-128 visible ASCII characters)."
    },
    {
      "code": "partner_api.rate_limited",
      "status": 429,
      "meaning": "Too many requests. Wait for `Retry-After` seconds."
    },
    {
      "code": "partner_api.quota_exceeded",
      "status": 429,
      "meaning": "The integration's daily request quota is used up."
    },
    {
      "code": "partner_api.disabled",
      "status": 503,
      "meaning": "The partner API (or this capability) is not available on this deployment."
    }
  ]
}
