> For the complete documentation index, see [llms.txt](https://docs.reachplatform.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.reachplatform.com/technical/bolt-on-setup/landing-page-to-hosted-checkout-integration.md).

# Landing Page to Hosted Checkout Integration

Implement a landing page to checkout integration for Reach using a browser form POST or query parameters, prefilled checkout data, promo codes, and secure redirect handling.

Use this guide to send customers from a campaign landing page, partner landing page, or brand site directly into Reach hosted checkout.

This direct-to-checkout integration supports prefilled plan selection, promo codes, return URLs, and campaign tracking.

{% hint style="warning" %}
Before launch, Reach must whitelist the source domain customers come from before this integration can be enabled. Ask your account manager to set this up.
{% endhint %}

## Integration methods

* **POST** — Form POST with encoded payload. Your landing page builds a JSON payload, encodes it as base64url, and submits it via a browser form POST. Supports richer data including per-line customer details, SIM preferences, and granular checkout config. Payload is never exposed in the URL.
* **GET** — URL query parameters. Simpler integration using standard URL query parameters. Suitable for lightweight campaigns where only plan, line count, promo, and return URL need to be passed. Best for static links in emails, banners, and QR codes.

{% hint style="info" %}
Reach recommends the **POST method** for all new integrations. It supports a richer payload, does not expose checkout data in the URL or browser history, and uses a secure `httpOnly` session cookie for handoff. Use the GET method only when a static link is required and richer payload fields are not needed.
{% endhint %}

## POST method for landing page to checkout integration

**Recommended**

Your landing page builds a checkout payload as JSON, encodes it as base64url, and submits it to the Reach endpoint via a browser form POST. Reach validates the payload, creates a secure session, and issues a 302 redirect into the matching hosted checkout flow.

The `/lander` endpoint is public and accepts unauthenticated POST requests. The browser sends the request as `application/x-www-form-urlencoded`. Do not send checkout payloads in the URL.

### Endpoint

`POST /lander`

Send a single `payload` field that contains the base64url-encoded JSON checkout object for the checkout handoff.

### How it works

{% stepper %}
{% step %}

### Build the checkout payload

Create a JSON object describing the checkout type, cart contents, and redirect behavior.
{% endstep %}

{% step %}

### Encode the payload

Encode the full JSON string using RFC 4648 base64url — replace `+` with `-`, replace `/` with `_`, and strip trailing `=`.
{% endstep %}

{% step %}

### Submit a browser form POST

Post the encoded value as the `payload` hidden field to `/lander`.
{% endstep %}

{% step %}

### Reach validates and creates a session

Reach decodes the payload, validates all fields, and sets a secure `httpOnly` cookie.
{% endstep %}

{% step %}

### User is redirected into checkout

Reach sends a 302 redirect to the matching checkout flow with all fields pre-filled.
{% endstep %}
{% endstepper %}

### Supported checkout types

| type value     | Checkout route |
| -------------- | -------------- |
| `new_purchase` | /checkout      |

### POST payload structure

| Field                  | Required        | Description                                                                                                                                                                                                                                                                                   |
| ---------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type string            | Required        | Reach checkout flow to open. See supported types above.                                                                                                                                                                                                                                       |
| purchaseData array     | planID required | Array of line objects. Each object represents one customer line. Only one object must include `isPrimary: true`. If multiple are available, then first object is taken as primary line. Subsequent objects are treated as secondary customers. Only `planID` is mandatory within each object. |
| returnUrl string (URL) | Required        | HTTPS page to redirect to after checkout completes, is cancelled, or errors. Must include the `https://` protocol.                                                                                                                                                                            |
| promo string           | Optional        | Promo or coupon code to pre-apply at checkout. Validated against the brand catalogue on load. e.g. **SAVE10**                                                                                                                                                                                 |
| purchaseConfig object  | Optional        | Controls which checkout elements the user can modify. See `purchaseConfig` fields below.                                                                                                                                                                                                      |
| utm\_\* string         | Optional        | Standard UTM parameters (`utm_source`, `utm_campaign`, etc.) and custom brand attribution parameters. Stored in session and forwarded to your configured analytics destination.                                                                                                               |

#### purchaseData line fields

| Field             | Required | Description                                                                   |
| ----------------- | -------- | ----------------------------------------------------------------------------- |
| planID string     | Required | Must match the Reach catalogue exactly. e.g. **REACH\@brandname\@By the Gig** |
| isPrimary boolean | Optional | Must be `true` on the first purchaseData object.                              |
| firstName string  | Optional | Pre-fills the first name field for this line.                                 |
| lastName string   | Optional | Pre-fills the last name field for this line.                                  |
| emailID string    | Optional | Pre-fills the email address for this line.                                    |
| simType string    | Optional | Pre-selects SIM type. Accepted values: `eSIM`, `pSIM`.                        |

#### purchaseConfig fields

| Field                     | Default   | Description                                                      |
| ------------------------- | --------- | ---------------------------------------------------------------- |
| allowChangePlan boolean   | **false** | Whether the user can switch to a different plan during checkout. |
| allowChangeCoupon boolean | **false** | Whether the user can add or modify the promo code.               |
| allowAddLine boolean      | **false** | Whether the user can add additional lines to the cart.           |

### Encode the checkout payload

Convert the JSON object to a string and encode it as RFC 4648 base64url before submitting.

```javascript
function base64urlEncode(str) {
  const utf8 = new TextEncoder().encode(str);
  let binary = "";
  for (const byte of utf8) binary += String.fromCharCode(byte);
  return btoa(binary)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

const encodedPayload = base64urlEncode(JSON.stringify(payload));
```

### Example payload

```json
{
  "type": "new_purchase",
  "purchaseData": [
    {
      "planID": "REACH@brandname@By the Gig",
      "firstName": "Jane",
      "lastName": "Doe",
      "emailID": "jane@example.com",
      "simType": "eSIM",
      "isPrimary": true
    }
  ],
  "promo": "SAVE10",
  "returnUrl": "https://your-site.com/thank-you",
  "purchaseConfig": {
    "allowChangePlan": false,
    "allowChangeCoupon": false,
    "allowAddLine": false,
  },
  "utm_source": "summer_campaign",
  "utm_campaign": "by_the_gig"
}
```

{% hint style="info" %}
`planID` must match the Reach catalogue exactly. `returnUrl` must be a valid HTTPS URL.
{% endhint %}

### Submit the landing page form POST

Post the encoded value in a hidden `payload` field. The end-to-end example below builds and submits the form when the user clicks the button.

```html
<!DOCTYPE html>
<html>
  <body>
    <button id="checkoutBtn">Continue to Checkout</button>
    <script>
      function base64urlEncode(str) {
        const utf8 = new TextEncoder().encode(str);
        let binary = "";
        for (const byte of utf8) binary += String.fromCharCode(byte);
        return btoa(binary)
          .replace(/\+/g, "-")
          .replace(/\//g, "_")
          .replace(/=+$/, "");
      }

      document.getElementById("checkoutBtn").addEventListener("click", function () {
        const payload = {
          type: "new_purchase",
          purchaseData: [{ planID: "REACH@brandname@By the Gig", simType: "eSIM", isPrimary: true }],
          promo: "SAVE10",
          returnUrl: "https://your-site.com/thank-you",
          purchaseConfig: {
            allowChangePlan: false,
            allowChangeCoupon: false,
            allowAddLine: false,
          }
        };

        const encoded = base64urlEncode(JSON.stringify(payload));
        const form = document.createElement("form");
        form.method = "POST";
        form.action = "https://www.brandname.thatsmymobile.com/api/lander";

        const input = document.createElement("input");
        input.type = "hidden";
        input.name = "payload";
        input.value = encoded;

        form.appendChild(input);
        document.body.appendChild(form);
        form.submit();
      });
    </script>
  </body>
</html>
```

{% hint style="info" %}
**Pre-launch checklist** — Validate every `planID` against your brand catalogue before going live. Test the full redirect flow in QA. Prefer server-side payload generation where possible. Always supply a valid `returnUrl`.
{% endhint %}

## GET method for direct checkout links

A simpler integration using URL query parameters. No payload encoding is required. This method is best for static direct checkout links in emails, QR codes, banners, and paid campaigns where richer per-line data is not needed. It is not recommended when passing customer details or requiring granular checkout controls.

All parameters are passed as query strings on the `/lander` path. No request body is used. Always URL-encode parameter values before constructing the link.

{% hint style="warning" %}
Query parameters are visible in browser history, server logs, and referrer headers. Do not use the GET method to pass sensitive customer data. For those use cases, use the POST method instead.
{% endhint %}

### Endpoint

`GET /lander`

All parameters passed as URL query strings.

### Sample URL

`/lander?type=new_purchase&planId=BytheGig&lines=1&promo=SUMMER25&returnUrl=https://brandmobile.com`

### GET query parameters for checkout links

| Parameter | Type         | Required | Description                                                                                                                                                    |
| --------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type      | string       | Required | Specifies the purchase intent and checkout flow to initialize. Accepted values: **new\_purchase**                                                              |
| planId    | string       | Required | The plan to pre-select. Must match the Reach catalogue exactly. URL-encode this value — it typically contains spaces and special characters. e.g. **BytheGig** |
| lines     | integer      | Optional | Number of lines to pre-fill. Default: **1**                                                                                                                    |
| promo     | string       | Optional | Promotional code to automatically apply. If invalid or expired, checkout continues without a discount e.g. **SUMMER25**                                        |
| returnUrl | string (URL) | Required | Fully qualified HTTPS URL to redirect to after checkout. e.g. **<https://brandmobile.com>**                                                                    |

### Checkout behavior

**Plan pre-selection**

The plan specified by `planId` is pre-selected. The customer lands directly on checkout, bypassing the plan-list page.

**Lines pre-fill**

Quantity is initialized to `lines`. Defaults to 1 if omitted.

**Promo application**

A valid `promo` is applied automatically. Invalid codes are silently ignored — checkout is not blocked.

**Post-checkout**

On completion or cancellation, the user is taken through the rest of the usual Reach journey.

### Implementation example

```javascript
const buildLanderUrl = (baseUrl, params) => {
  const required = ["type", "planId", "returnUrl"];
  required.forEach(key => {
    if (!params[key]) throw new Error(`Missing required param: ${key}`);
  });

  const query = new URLSearchParams({
    type: params.type,
    planId: params.planId,
    lines: params.lines ?? 1,
    returnUrl: params.returnUrl,
    ...(params.promo && { promo: params.promo }),
  });

  return `${baseUrl}/lander?${query.toString()}`;
};

// Usage
const url = buildLanderUrl("https://brandmobile.com", {
  type: "new_purchase",
  planId: "REACH@nu-qa@By the Gig",
  lines: 2,
  promo: "SUMMER25",
  returnUrl: "https://brandmobile.com",
});
```

## Operational notes for landing page and checkout redirect flows

* **Back navigation** — Open the checkout link in the same browser tab as the landing page so the back button returns the user to your campaign page correctly.
* **Session restoration** — If a customer leaves checkout and returns, their plan, promo, and UTM context are restored. The promo is re-validated on return; if expired, the customer is notified and the updated total is shown.
* **Promo failure behavior** — An invalid or expired promo never blocks checkout. The session proceeds without a discount and, where applicable, the customer is shown a notification.
* **Brand theming** — The correct brand theme is applied automatically based on the domain in the request URL.
* **Support** — Questions or integration issues? Contact your account manager or email `accounts@reachmobile.com`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.reachplatform.com/technical/bolt-on-setup/landing-page-to-hosted-checkout-integration.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
