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

# Showing Order Details

> Display order history, products, tracking, and shipping information in your members area

ElasticFunnels provides two ways to display order information on your pages: the `<order>` HTML tag (for the visual builder) and backend functions like `getOrders()` / `getOrder()` / `getSessionOrders()` / `getPurchasedProducts()` for code-based pages. Both retrieve data from the customer's purchase history.

## Two Approaches

| Approach                     | Best for                                                                        | Used in                       |
| ---------------------------- | ------------------------------------------------------------------------------- | ----------------------------- |
| `<order>` tag                | Visual builder pages, simple layouts                                            | Drag-and-drop editor          |
| `getOrders()` / `getOrder()` | Code-based pages, full order history                                            | Backend scripts / `.ef` pages |
| `getSessionOrders()`         | Thank-you pages — current purchase only                                         | Backend scripts / `.ef` pages |
| `getPurchasedProducts()`     | Member-area "library" pages — purchased products with their bonuses + downloads | Backend scripts / `.ef` pages |

<Tip>
  For a member-area page that needs **each purchased product with its included bonuses and download links**, prefer `getPurchasedProducts()` over manually joining `getOrders()` + `getAllProducts()` + `getBonusProducts()` on the client. It returns a flat list with same-order bonuses already attached and a pre-rolled-up `downloads[]` array (each entry tagged with `kind`). See the [data-functions reference](/backend-scripts/data-functions#getpurchasedproducts) and the ["My Library" pattern](/members-area/overview) for examples.
</Tip>

## Using the `<order>` Tag

The `<order>` tag is processed server-side and automatically loops through the customer's purchase history. See the full [Order Tag](/pages/order-tag) reference for all placeholders and options.

```html theme={null}
<order sort-by="newest" limit="5">
  <h3>Order #{order_number}</h3>
  <p>Date: {order_date}</p>
  <p>Shipping: {shipping_address}</p>

  <order-product>
    <img src="{product_image_url}" alt="{product_name}" />
    <p>{product_name} — {price}</p>
  </order-product>

  <order-bonus-product>
    <p>Bonus: {bonus_product_name}</p>
    <a href="{bonus_download}">Download</a>
  </order-bonus-product>
</order>
```

## Using `getOrders()` in Backend Scripts

For code-based pages (`.ef` files or pages with backend scripts), `getOrders()` gives you full control over the layout. It returns an array of order objects you can loop through with `@foreach`.

### Basic Usage

```html theme={null}
<script scope="backend">
var orders = getOrders('newest', 10);
setVariable('orders', orders);
</script>

@if(orders.length gt 0)
  @foreach(order in orders)
  <div class="order-card">
    <h3>Order #{{ order.order_number }}</h3>
    <p>{{ order.created_at | date:'dd, MMM yyyy' }}</p>

    @foreach(product in order.products)
    <div class="product-row">
      <img src="{{ product.image_url }}" alt="{{ product.name }}" />
      <span>{{ product.name }}</span>
      <span>{{ product.price | formatPrice:product.currency_code }}</span>
    </div>
    @endforeach
  </div>
  @endforeach
@else
  <p>No orders found.</p>
@endif
```

### Parameters (`getOrders`)

| Parameter | Type     | Default    | Description                                   |
| --------- | -------- | ---------- | --------------------------------------------- |
| `sortBy`  | `string` | `'newest'` | Sort order: `'newest'` or `'oldest'`          |
| `limit`   | `number` | `50`       | Maximum number of orders to return (max 1000) |

### Alternative: Inline Call

You can also call `getOrders()` directly in a `@set` directive without a backend script block:

```html theme={null}
@set(orders = getOrders('newest', 5))

@foreach(order in orders)
  <p>#{{ order.order_number }} — {{ order.created_at | date:'dd MMM yyyy' }}</p>
@endforeach
```

## Using `getOrder()` for a Single Order Detail Page

When you already know which order to show, use `getOrder(code)` instead of loading the full order list and searching manually. This is ideal for pages like `/members-order?order=EF-12345`.

`getOrder()` uses the same customer/session scope as `getOrders()`, so it only returns orders that belong to the currently logged-in customer for that brand.

### Example: Single order page

```html theme={null}
<script scope="backend">
var code = request.query.order;
var order = getOrder(code);
setVariable('order', order);
</script>

@if(order)
  <h1>Order #{{ order.order_number }}</h1>
  <p>{{ order.created_at | date:'dd MMM yyyy' }}</p>

  @foreach(product in order.products)
  <div>
    <span>{{ product.name }}</span>
    <span>{{ product.price | formatPrice:product.currency_code }}</span>
  </div>
  @endforeach
@else
  <p>Order not found.</p>
@endif
```

### What `code` can be

`getOrder(code)` matches any of these values within the current customer's orders:

* `order_number` / public order ID
* internal `order_id`
* conversion `code`

### Parameters

| Parameter | Type     | Required | Description                                                     |
| --------- | -------- | -------- | --------------------------------------------------------------- |
| `code`    | `string` | Yes      | Public order ID, internal order ID, or conversion code to match |

<Tip>
  For an order detail page, prefer `getOrder(request.query.order)` over `getOrders()` plus a manual loop/filter. It is shorter, clearer, and communicates intent directly.
</Tip>

## Fulfillment status and shipments

Use **`getOrderFulfillment(code)`** on an **order detail** page to show 3PL fulfillment status and timestamps. Responses are **allowlisted** (no raw provider payloads).

Use **`getOrderFulfillments()`** with **no arguments** to list **every** tracking row across **all** of the customer’s orders—each row includes `order_number` so you can link to `/members-order?order=…`.

For a **single** order, `order.tracking` from `getOrder` / `getOrders` is often enough for carrier links. The fulfillment helpers add **lifecycle status** (`getOrderFulfillment`) and a **cross-order shipment list** (`getOrderFulfillments`).

### Example: order detail with fulfillment summary

```html theme={null}
<script scope="backend">
var code = request.query.order;
setVariable('order', getOrder(code));
setVariable('fulfillment', getOrderFulfillment(code));
</script>

@if(fulfillment)
  <p>Fulfillment: {{ fulfillment.status }}</p>
  @if(fulfillment.sent_at)
    <p>Sent: {{ fulfillment.sent_at | date:'dd MMM yyyy' }}</p>
  @endif
@endif
```

### Example: all shipments for the account

```html theme={null}
<script scope="backend">
setVariable('shipments', getOrderFulfillments());
</script>

@if(shipments.length gt 0)
  @foreach(row in shipments)
  <div>
    <a href="/members-order?order={{ row.order_number }}">Order #{{ row.order_number }}</a>
    — {{ row.carrier }}: <a href="{{ row.tracking_url }}" target="_blank" rel="noopener">{{ row.tracking_number }}</a>
  </div>
  @endforeach
@endif
```

### Order Object Structure

Each order returned by `getOrders()` or `getOrder()` contains:

```javascript theme={null}
{
  order_number: "EF-12345",        // Public order ID
  created_at: "2026-03-15T...",    // ISO date string
  currency: "USD",                 // Order currency code

  // Billing address
  billing_address: "123 Main St",
  billing_city: "New York",
  billing_state: "NY",
  billing_zip: "10001",
  billing_country: "US",

  // Shipping address
  shipping_address: "123 Main St",
  shipping_address2: "",
  shipping_city: "New York",
  shipping_state: "NY",
  shipping_zip: "10001",
  shipping_country: "US",

  // Customer info
  customer_name: "John Doe",
  customer_email: "john@example.com",

  // Products array
  products: [
    {
      name: "Premium Course",
      image_url: "https://...",
      price: 97.00,
      quantity: 1,
      currency_code: "USD",
      is_bonus: false
    }
  ],

  // Tracking array (if fulfilled)
  tracking: [
    {
      tracking_number: "9400111899223456789012",
      tracking_url: "https://t.17track.net/en#nums=...",
      carrier: "USPS"
    }
  ]
}
```

## Displaying Tracking Information

If your orders have physical shipments, tracking information is automatically extracted from fulfillment data. The system checks multiple sources for tracking numbers:

1. Direct conversion fields (`tracking_number`, `tracking_url`)
2. Fulfillment provider response data
3. Fulfillment order data

Carriers are auto-detected from tracking number patterns (USPS, FedEx, UPS, DHL, UniUni). If no tracking URL is provided, a universal tracking link via 17track.net is generated.

`order.tracking` is always an array. When the order has not yet been fulfilled it is empty (`[]`). Use `order.shipping_address` as the signal that the order contains a physical product — if there is a shipping address but no tracking yet, show a "being processed" message.

### Example: Physical order with processing/tracking states

```html theme={null}
@set(orders = getOrders('newest', 5))

@foreach(order in orders)
<div class="order">
  <h3>Order #{{ order.order_number }}</h3>
  <p>{{ order.created_at | date:'dd MMM yyyy' }}</p>

  @if(order.shipping_address)
  <div class="shipping-address">
    <h4>Ships to</h4>
    <p>{{ order.shipping_address }}</p>
    <p>{{ order.shipping_city }}, {{ order.shipping_state }} {{ order.shipping_zip }}</p>
    <p>{{ order.shipping_country }}</p>
  </div>

  @if(order.tracking.length gt 0)
  <div class="tracking-info">
    <h4>Shipment Tracking</h4>
    @foreach(t in order.tracking)
    <div class="tracking-row">
      <span>{{ t.carrier }}</span>
      <a href="{{ t.tracking_url }}" target="_blank">{{ t.tracking_number }}</a>
    </div>
    @endforeach
  </div>
  @else
  <div class="tracking-pending">
    <p>Your order is being processed. Tracking information will appear here once your order ships.</p>
  </div>
  @endif

  @endif
</div>
@endforeach
```

The key pattern:

* `order.shipping_address` is present → physical product → show the shipping address block.
* Inside that block, `order.tracking.length gt 0` → shipment is on the way → show carrier + tracking link.
* Otherwise → show "Your order is being processed."

<Tip>
  Tracking numbers are auto-linked to [17track.net](https://17track.net) when no carrier URL is provided. The carrier is auto-detected from the number pattern (USPS, UPS, FedEx, DHL, UniUni). You do not need to map carriers yourself.
</Tip>

## `getSessionOrders()` — Current Session Orders

`getSessionOrders()` returns only the orders that were placed during the current browser session. Unlike `getOrders()`, which returns the full purchase history for a customer, `getSessionOrders()` is scoped to the active checkout session — making it the right choice for thank-you pages where you only want to show what was just bought.

By default it returns **all** orders from the current session. Pass an optional `limit` to cap the count.

### Parameters (`getSessionOrders`)

| Parameter | Type     | Default    | Description                                                           |
| --------- | -------- | ---------- | --------------------------------------------------------------------- |
| `sortBy`  | `string` | `'newest'` | Sort order: `'newest'` or `'oldest'`                                  |
| `limit`   | `number` | *(all)*    | Maximum number of orders to return; omit to return all session orders |

### Basic Usage

```html theme={null}
@set(orders = getSessionOrders())

@foreach(order in orders)
<div class="order-summary">
  <p>Order #{{ order.order_number }}</p>
  @foreach(product in order.products)
  <div>{{ product.name }} — {{ product.price | formatPrice:product.currency_code }}</div>
  @endforeach
</div>
@endforeach
```

<Tip>
  Prefer `getSessionOrders()` over `getOrders('newest', 1)` on thank-you pages. A customer with an existing purchase history could see a previous order with `getOrders` if the new conversion hasn't been indexed yet. `getSessionOrders()` is scoped to the current session so it only shows what was just purchased.
</Tip>

## Thank You Pages

Thank-you pages are a natural place to show order details. Since the customer is auto-authenticated from checkout, order data is available immediately.

Use **`getSessionOrders()`** on thank-you pages so that only the orders from the current checkout session are shown — not the customer's full history.

### Example: Thank You Page with Order Summary

```html theme={null}
@set(orders = getSessionOrders())

<h2>Order Confirmed!</h2>
<p>Thank you for your purchase.</p>

@if(orders.length gt 0)
  @foreach(order in orders)
  <div class="order-summary">
    <p>Order #{{ order.order_number }}</p>
    <p>Date: {{ order.created_at | date:'dd, MMM yyyy' }}</p>

    @foreach(product in order.products)
    <div class="product">
      <span>{{ product.name }}</span>
      <span>{{ product.price | formatPrice:product.currency_code }}</span>
    </div>
    @endforeach
  </div>
  @endforeach
@else
  <div class="processing">
    <p>Your order is still being processed. This page will refresh automatically.</p>
  </div>
@endif

<a href="/members">Access Member Area</a>
```

<Tip>
  If the order hasn't been indexed yet when the thank-you page loads, `getSessionOrders()` may return empty. Add a JavaScript auto-refresh timer so the page retries after a few seconds — the order typically appears within 5–10 seconds.
</Tip>

## Customer Data Outside of Orders

You can also display customer information directly using the `customer` template object (populated from the session):

```html theme={null}
<p>Welcome back, {{ customer.name|default:"Member" }}!</p>
<p>Email: {{ customer.email }}</p>
```

The `customer` object includes: `name`, `email`, `first_name`, `last_name`, `phone`, `order_id`, `order_ids`, and full billing/shipping address fields. It is automatically available in every backend script and template — no function call needed.

## Order Grouping

Orders are grouped intelligently — multiple conversions from the same purchase session (e.g., a main product and an upsell bought in the same checkout flow) are combined into a single order. The grouping logic uses:

* **Session ID matching** — Conversions from the same browser session
* **Time proximity** — Conversions within 6 hours of each other

This means an upsell purchased immediately after the main product shows under the same order, not as a separate entry.

## Related Documentation

<CardGroup cols={2}>
  <Card title="Order Tag Reference" icon="receipt" href="/pages/order-tag">
    Full reference for the `<order>` HTML tag and all placeholders
  </Card>

  <Card title="Backend Scripts" icon="terminal" href="/backend-scripts/overview">
    Learn about backend scripts and available functions
  </Card>

  <Card title="Template Engine Syntax" icon="code" href="/frontend-template-engine/syntax">
    Filters, conditionals, and loops for displaying data
  </Card>

  <Card title="Thank You Pages" icon="check-circle" href="/forms/thank-you-pages">
    Best practices for post-purchase confirmation pages
  </Card>
</CardGroup>
