> ## Documentation Index
> Fetch the complete documentation index at: https://docs.d-edge.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> This site documents the D-EDGE APIs for technology partners (web agencies, OTAs, PMS and RMS vendors, booking and CRM tools). It is written for developers integrating with hotels that use the D-EDGE platform.
> Seven APIs, grouped by domain. Connectivity: Channel Manager API (distribute a hotel's inventory to your channel and send bookings back). Pricing & Availability: Best Price API (best offers to highlight), Quotation API (all bookable offers for a stay), Daily Rates API (daily availability between two dates). Booking: Booking Engine API (a hotel's full direct booking funnel), Gift Shop API (gift vouchers from catalogue to payment). Guest Management: CRM API (guest profiles).
> Pick the API by the need: to display prices, use the Best Price API or the Daily Rates API; to quote a specific stay, use the Quotation API; to book, use the Booking Engine API; to sell gift vouchers, use the Gift Shop API; to manage guest profiles, use the CRM API; to receive hotel inventory and send bookings back as a distribution channel, use the Channel Manager API.
> Base URLs, hotel identifiers, and credentials are delivered by the D-EDGE partnership team during onboarding. Do not invent base URLs, endpoints, parameters, or example credentials that do not appear on these pages.
> Authentication differs per API: OAuth 1.0 for Best Price, Quotation, Daily Rates, and Booking Engine; an X-API-KEY header for Gift Shop; OAuth 2.0 client credentials for CRM; partner-defined credentials carried in the XML message for the Channel Manager API. Read each API's Authentication page before writing code.
> A mock environment returns realistic but fictitious data without credentials. The Postman and Insomnia collections on the API collections page target it by default. Use it for exploration and tests, never for production traffic.
> Use the official names exactly: D-EDGE, D-EDGE platform, Channel Manager API, partnership team. Do not use Back Office or Extranet.
> Each API has a Changelog page. Check it for the current behaviour before relying on an older answer. For questions these pages do not answer, direct the reader to the Contact us page.

# Promo code landing

> Quote the offer behind a promo code on a tailored landing page

A promo code distributed through email, social, or a partner channel converts best when its landing page surfaces the offer behind it directly. One Stay quotation call with `referenceRateCode` set returns only the rate the code unlocks, ready to render.

## Prerequisites

Before building the landing page, make sure you have:

* A working Quotation call. If not, follow the [Quick start](/quotation-api/get-started/quick-start.md) first.
* A hotel ID and a group ID for the property the campaign points at.
* An arrival date and a night count, supplied by the campaign URL or a small form on the page.
* A rate code or private offer code (for example `AUTUMN2026`), shared by the revenue manager who set the offer up.

## How it works

The Stay quotation endpoint accepts a `referenceRateCode` filter on the `ratePlans` entry. With it set, the API restricts the quote to the matching rate plan or private offer code: a public rate code like `BAR` returns the public offer, a private code like `AUTUMN2026` returns the campaign-only or negotiated offer. Without it, the endpoint returns the whole catalogue, which is the [Bookable offers showcase](/quotation-api/resources/use-cases/bookable-offers-showcase.md) pattern.

Two outcomes worth handling on the page:

* **Code applies**: the response carries one rate plan with one or more entities. Render the offer.
* **Code does not apply** (expired, restricted to other dates, wrong hotel, wrong party): the response is a `200 OK` with empty `ratePlans` or no `Available` entity. Show a fallback explaining the code is not valid for the chosen stay, rather than a generic error.

A single call covers the whole landing page. Cancellation conditions and included articles arrive in the same response, and adding `TaxesAndFees` to the layout brings the tax breakdown with them, so the page stays one round trip away from the conversion CTA.

## Build the landing page

> [!NOTE]
>
> The examples below assume the OAuth signing setup from the [Quick start](/quotation-api/get-started/quick-start.md).

### 1. Quote the stay with the code applied

Call the Stay quotation endpoint once with the chosen stay and the rate code on `ratePlans[].referenceRateCode`. Keep `layout.levels[*].properties` to the columns the landing page actually displays.

```bash cURL theme={null}
curl --request POST \
  --url https://{apiBaseUrl}/Planning/2018A/Quotation/GetStayPlanning \
  --header 'Authorization: OAuth ...' \
  --header 'Content-Type: application/json' \
  --data '{
    "arrivalDate": "2026-12-07",
    "nightCount": 3,
    "guests": [{"adults": 2}],
    "filter": {
      "ratePlans": [{
        "groupId": <groupId>,
        "referenceRateCode": "AUTUMN2026",
        "hotels": {"exceptions": [<hotelId>], "default": "Excluded"}
      }],
      "currencies": {"exceptions": ["EUR"], "default": "Excluded"}
    },
    "layout": {
      "levels": [{
        "name": "ArticleRate",
        "properties": [
          "Status",
          "TotalPrice",
          "IncludedArticles",
          "CancellationCondition",
          "Occupancies"
        ]
      }]
    }
  }'
```

```javascript Node.js theme={null}
const url = "https://{apiBaseUrl}/Planning/2018A/Quotation/GetStayPlanning";

const response = await fetch(url, {
  method: "POST",
  headers: { ...authHeaders, "Content-Type": "application/json" },
  body: JSON.stringify({
    arrivalDate: "2026-12-07",
    nightCount: 3,
    guests: [{ adults: 2 }],
    filter: {
      ratePlans: [
        {
          groupId: <groupId>,
          referenceRateCode: "AUTUMN2026",
          hotels: { exceptions: [<hotelId>], default: "Excluded" },
        },
      ],
      currencies: { exceptions: ["EUR"], default: "Excluded" },
    },
    layout: {
      levels: [
        {
          name: "ArticleRate",
          properties: [
            "Status",
            "TotalPrice",
            "IncludedArticles",
            "CancellationCondition",
            "Occupancies",
          ],
        },
      ],
    },
  }),
});
const data = await response.json();
```

```python Python theme={null}
response = session.post(
    "https://{apiBaseUrl}/Planning/2018A/Quotation/GetStayPlanning",
    json={
        "arrivalDate": "2026-12-07",
        "nightCount": 3,
        "guests": [{"adults": 2}],
        "filter": {
            "ratePlans": [
                {
                    "groupId": <groupId>,
                    "referenceRateCode": "AUTUMN2026",
                    "hotels": {"exceptions": [<hotelId>], "default": "Excluded"},
                }
            ],
            "currencies": {"exceptions": ["EUR"], "default": "Excluded"},
        },
        "layout": {
            "levels": [
                {
                    "name": "ArticleRate",
                    "properties": [
                        "Status",
                        "TotalPrice",
                        "IncludedArticles",
                        "CancellationCondition",
                        "Occupancies",
                    ],
                }
            ]
        },
    },
)
data = response.json()
```

```php PHP theme={null}
$response = $client->post('Planning/2018A/Quotation/GetStayPlanning', [
    'json' => [
        'arrivalDate' => '2026-12-07',
        'nightCount'  => 3,
        'guests'      => [['adults' => 2]],
        'filter' => [
            'ratePlans' => [[
                'groupId'           => <groupId>,
                'referenceRateCode' => 'AUTUMN2026',
                'hotels'            => ['exceptions' => [<hotelId>], 'default' => 'Excluded'],
            ]],
            'currencies' => ['exceptions' => ['EUR'], 'default' => 'Excluded'],
        ],
        'layout' => [
            'levels' => [[
                'name' => 'ArticleRate',
                'properties' => [
                    'Status',
                    'TotalPrice',
                    'IncludedArticles',
                    'CancellationCondition',
                    'Occupancies',
                ],
            ]],
        ],
    ],
]);
$data = json_decode($response->getBody(), true);
```

```csharp C# theme={null}
var request = new RestRequest("/Quotation/GetStayPlanning", Method.Post);
request.AddJsonBody(new
{
    arrivalDate = "2026-12-07",
    nightCount = 3,
    guests = new[] { new { adults = 2 } },
    filter = new
    {
        ratePlans = new[]
        {
            new
            {
                groupId = <groupId>,
                referenceRateCode = "AUTUMN2026",
                hotels = new { exceptions = new[] { <hotelId> }, @default = "Excluded" }
            }
        },
        currencies = new { exceptions = new[] { "EUR" }, @default = "Excluded" }
    },
    layout = new
    {
        levels = new[]
        {
            new
            {
                name = "ArticleRate",
                properties = new[]
                {
                    "Status",
                    "TotalPrice",
                    "IncludedArticles",
                    "CancellationCondition",
                    "Occupancies"
                }
            }
        }
    }
});

var response = await client.ExecuteAsync(request);
var data = JsonDocument.Parse(response.Content!).RootElement;
```

```java Java theme={null}
String body = mapper.writeValueAsString(Map.of(
    "arrivalDate", "2026-12-07",
    "nightCount", 3,
    "guests", List.of(Map.of("adults", 2)),
    "filter", Map.of(
        "ratePlans", List.of(Map.of(
            "groupId", <groupId>,
            "referenceRateCode", "AUTUMN2026",
            "hotels", Map.of(
                "exceptions", List.of(<hotelId>),
                "default", "Excluded"
            )
        )),
        "currencies", Map.of(
            "exceptions", List.of("EUR"),
            "default", "Excluded"
        )
    ),
    "layout", Map.of(
        "levels", List.of(Map.of(
            "name", "ArticleRate",
            "properties", List.of(
                "Status",
                "TotalPrice",
                "IncludedArticles",
                "CancellationCondition",
                "Occupancies"
            )
        ))
    )
));

HttpPost request = new HttpPost("https://{apiBaseUrl}/Planning/2018A/Quotation/GetStayPlanning");
request.setEntity(new StringEntity(body, "UTF-8"));
request.setHeader("Content-Type", "application/json");
consumer.sign(request);

JsonNode data;
try (CloseableHttpResponse response = client.execute(request)) {
    data = mapper.readTree(EntityUtils.toString(response.getEntity()));
}
```

### 2. Detect a missing or invalid code

A `200 OK` with no rate plan, or a rate plan with no `Available` entity, means the code does not apply to the chosen stay. Treat it as a UX state, not an error: explain that the code is not valid for these dates or this hotel, and offer the public catalogue as a fallback.

```javascript Node.js theme={null}
const ratePlan = data.ratePlans?.[0];
const offer = ratePlan?.hotels
  ?.flatMap(hotel => hotel.entities)
  ?.find(entity => entity.status === "Available");

if (!offer) {
  renderCodeNotValid({ code: "AUTUMN2026" });
  return;
}
```

```python Python theme={null}
rate_plan = (data.get("ratePlans") or [None])[0]
offer = next(
    (
        entity
        for hotel in (rate_plan or {}).get("hotels", [])
        for entity in hotel.get("entities", [])
        if entity.get("status") == "Available"
    ),
    None,
)

if offer is None:
    render_code_not_valid({"code": "AUTUMN2026"})
    return
```

```php PHP theme={null}
$ratePlan = $data['ratePlans'][0] ?? null;
$offer = null;
foreach ($ratePlan['hotels'] ?? [] as $hotel) {
    foreach ($hotel['entities'] ?? [] as $entity) {
        if (($entity['status'] ?? null) === 'Available') {
            $offer = $entity;
            break 2;
        }
    }
}

if ($offer === null) {
    renderCodeNotValid(['code' => 'AUTUMN2026']);
    return;
}
```

```csharp C# theme={null}
var ratePlans = data.GetProperty("ratePlans");
JsonElement? offer = null;

if (ratePlans.GetArrayLength() > 0)
{
    foreach (var hotel in ratePlans[0].GetProperty("hotels").EnumerateArray())
    {
        foreach (var entity in hotel.GetProperty("entities").EnumerateArray())
        {
            if (entity.TryGetProperty("status", out var status)
                && status.GetString() == "Available")
            {
                offer = entity;
                break;
            }
        }
        if (offer.HasValue) break;
    }
}

if (!offer.HasValue)
{
    RenderCodeNotValid(new { code = "AUTUMN2026" });
    return;
}
```

```java Java theme={null}
JsonNode ratePlan = data.path("ratePlans").path(0);
JsonNode offer = null;
for (JsonNode hotel : ratePlan.path("hotels")) {
    for (JsonNode entity : hotel.path("entities")) {
        if ("Available".equals(entity.path("status").asText())) {
            offer = entity;
            break;
        }
    }
    if (offer != null) break;
}

if (offer == null) {
    renderCodeNotValid(Map.of("code", "AUTUMN2026"));
    return;
}
```

### 3. Render the offer card

Display the room, total price, included articles, and cancellation condition. The `referenceRateCode` echoed in the response confirms the code applied is the one you sent, useful for analytics or partner attribution.

```javascript Node.js theme={null}
renderOfferCard({
  code: ratePlan.referenceRateCode,
  roomId: offer.roomId,
  rateId: offer.rateId,
  totalPrice: offer.totalPrice,
  currency: offer.currency,
  includedArticles: offer.articles?.includedArticles ?? [],
  cancellation: offer.cancellationCondition,
});
```

```python Python theme={null}
render_offer_card({
    "code": rate_plan["referenceRateCode"],
    "roomId": offer["roomId"],
    "rateId": offer["rateId"],
    "totalPrice": offer["totalPrice"],
    "currency": offer["currency"],
    "includedArticles": offer.get("articles", {}).get("includedArticles", []),
    "cancellation": offer.get("cancellationCondition"),
})
```

```php PHP theme={null}
renderOfferCard([
    'code'             => $ratePlan['referenceRateCode'],
    'roomId'           => $offer['roomId'],
    'rateId'           => $offer['rateId'],
    'totalPrice'       => $offer['totalPrice'],
    'currency'         => $offer['currency'],
    'includedArticles' => $offer['articles']['includedArticles'] ?? [],
    'cancellation'     => $offer['cancellationCondition'] ?? null,
]);
```

```csharp C# theme={null}
RenderOfferCard(new
{
    code = ratePlans[0].GetProperty("referenceRateCode").GetString(),
    roomId = offer.Value.GetProperty("roomId").GetInt32(),
    rateId = offer.Value.GetProperty("rateId").GetInt32(),
    totalPrice = offer.Value.GetProperty("totalPrice").GetDecimal(),
    currency = offer.Value.GetProperty("currency").GetString()
});
```

```java Java theme={null}
renderOfferCard(Map.of(
    "code", ratePlan.path("referenceRateCode").asText(),
    "roomId", offer.path("roomId").asInt(),
    "rateId", offer.path("rateId").asInt(),
    "totalPrice", offer.path("totalPrice").asDouble(),
    "currency", offer.path("currency").asText()
));
```

## Tips

* **Pass the code through the URL, not a form**. A campaign link like `https://book.example.com/offer?code=AUTUMN2026&arrival=2026-12-07&nights=3` lets the page render the offer in one round-trip with no friction. Reserve a form for cases where the guest types a code into an existing booking funnel.
* **Log the code use even when no offer comes back**. Marketing teams need both numbers: how many people landed on the page with a given code, and how many of those got a valid offer. The gap surfaces an expired or misconfigured offer faster than a help ticket.

## Related

> **[Reference](/quotation-api/build/reference/stay-quotation.md)**
>
> Full schema for the Stay quotation endpoint.

> **[Best practices](/quotation-api/build/best-practices.md)**
>
> Caching, performance, and resilience patterns.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.