# Manual Recommendation Integration

> Current Froomle documentation · [View canonical page](https://docs.froomle.com/froomle-doc-main/v2/guide/manual-integration/recommendations.html)

Use this guide when your application requests and renders recommendations without the frontend SDK owning the request and rendering flow. This is the primary method for backend, mobile, and custom API integrations.

> **IMPORTANT:**
> 
> This step requires a synchronized [item catalog](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/item-sync-options.html). Page and business event tracking should already be live before recommendation placements are enabled.

<a id="_before_you_start"></a>

## Before you start

-   Define modules, list names, page types, channels, and expected response fields.
    
-   Review the [Recommendation Request Model](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/recommendation-requests.html).
    
-   Apply the [identity and consent contract](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/user-identity-consent.html).
    
-   Configure [OAuth authentication](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/authentication-flow.html) for the Recommendations API.
    

<a id="_request_lifecycle"></a>

## Request lifecycle

1.  Obtain and cache a valid bearer token on a trusted backend.
    
2.  Build the request from the agreed lists, page context, identity, histories, filters, and exclusions.
    
3.  Send the request to the environment-specific Recommendations API endpoint.
    
4.  Read the returned lists and preserve their attribution metadata.
    
5.  Render the selected items or apply the agreed fallback behavior.
    
6.  Send an `impression` for every recommendation shown and a `click_on_recommendation` for every recommendation clicked.
    

For endpoint paths, exact fields, and response schemas, see the [Recommendations API Reference](https://docs.froomle.com/froomle-doc-main/v2/api_reference/recommendations/index.html). To show more than the initial results, follow [pagination](https://docs.froomle.com/froomle-doc-main/v2/guide/manual-integration/recommendations.html#manual-recommendation-pagination).

<a id="_authentication_and_request_domain"></a>

## Authentication and request domain

Keep OAuth client credentials on a backend and send the resulting bearer token with authenticated recommendation requests. If you use a custom hostname, follow [Custom Domain (CNAME)](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/custom-domain-cname.html) and replace the default request host consistently.

> **NOTE:**
> 
> Recommendation requests require the OAuth bearer token described above. Tracked events remain unauthenticated and must not reuse the recommendation `Authorization` header.

<a id="_build_the_request"></a>

## Build the request

The request shape is shared across integration methods. Use [Recommendation Request Model](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/recommendation-requests.html) for the behavior of lists, aliases, context, histories, exclusions, filters, and response enrichment.

Ensure that:

-   `context_item` and history item IDs exist in the synchronized catalog.
    
-   `context_item_type` accompanies `context_item`.
    
-   `device_id` and `user_id` follow the shared identity and consent rules.
    
-   Each placement uses the agreed `list_name`, page type, and channel.
    

<a id="manual-recommendation-pagination"></a>

## Pagination

To paginate a recommendation result, request a ranked list and return only its first items in the initial response. Then use the returned `list_key` to fetch further slices. Pagination must be enabled for your environment; contact Froomle if you need it.

For example, send `POST /{environment}/recommendations/requests` with this list in the request body:

```json
{
  "list_name": "reco_list_001",
  "list_size": 5,
  "limit": 2
}
```

Include it in the request’s `lists` array with the usual page and identity fields. `list_size` requests a ranked list of up to five items; `limit` returns up to two of them in the initial response, starting at rank 1. Save the `list_key` in that response’s list alongside the attribution fields described below.

Fetch another slice with `GET /{environment}/recommendations/lists?list_key={list_key}&offset=3&limit=2`. Despite the route name, this is the pagination request: `offset` is the **1-based rank of the first item to return**, and `limit` is the maximum number of items to return. `offset` is neither a page number nor the number of items to skip.

| Request | `offset` | `limit` | Ranks returned if five items are available |
| --- | --- | --- | --- |
| Initial POST | — | 2 | 1, 2 |
| Next GET | 3 | 2 | 3, 4 |
| Following GET | 5 | 2 | 5 |
| GET from the beginning again | 1 | 2 | 1, 2 |

Use each response’s item ranks and attribution when rendering and tracking that slice. The list may contain fewer items than requested, and its `list_key` is available only for a limited time. An unknown or expired key returns `404`. See the [Pagination endpoint reference](https://docs.froomle.com/froomle-doc-main/v2/api_reference/recommendations/index.html#_recommendations_api_api_recommendations_get_precomputed) for the complete request and response schema.

<a id="_preserve_response_attribution"></a>

## Preserve response attribution

> **IMPORTANT:**
> 
> Do not discard `request_id`, `list_name`, or an applicable `user_group` before rendering. The returned `user_group` can be required to choose what to render even when it must be omitted from the outbound event. Manual recommendation events always retain request and placement attribution; they include `user_group` only for externally assigned customer/control traffic.

Your application owns rendering in this method. It must therefore retain enough state to associate every rendered item with the recommendation response that supplied it.

<a id="ManualRecommendationBenchmarking"></a>

## Benchmarking

Follow the shared [A/B testing and benchmarking](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/ab-testing-benchmarking.html) contract. When Froomle assigns the branch, use the returned `user_group` as the rendering authority. Do not echo that returned group in manually tracked events; Froomle assigns the version and group server-side. For customer-managed control traffic, pass the agreed externally assigned control group on the request and on its manually tracked recommendation events. For customer-managed treatment/Froomle traffic, omit the group from both. Do not invent or hard-code a `request_id`.

<a id="_froomle_managed_request_example"></a>

### Froomle-managed request example

Send a standard request to `POST /{environment}/recommendations/requests` without `user_group`:

```json
{
  "device_id": "device-123",
  "user_id": "user-123",
  "page_type": "home",
  "lists": [
    {
      "list_name": "home_page_trending",
      "list_size": 10
    }
  ]
}
```

For a customer-vs-Froomle benchmark, a customer/control response can contain an empty recommendation list:

```json
{
  "request_id": "req-abc-123",
  "user_group": "customer",
  "lists": [
    {
      "list_name": "home_page_trending",
      "items": []
    }
  ]
}
```

Render the customer/control content because `user_group` resolves that branch, not merely because `items` is empty.

<a id="_customer_managed_request_example"></a>

### Customer-managed request example

For the customer/control branch, pass the group name agreed with Froomle:

```json
{
  "device_id": "device-123",
  "user_id": "user-123",
  "page_type": "home",
  "user_group": "customer",
  "lists": [
    {
      "list_name": "home_page_trending",
      "list_size": 10
    }
  ]
}
```

For the treatment/Froomle branch, send the same request without `user_group` so normal Froomle version resolution applies.

<a id="_hybrid_browser_tracking"></a>

## Hybrid browser tracking

A backend may fetch recommendations while the frontend SDK tracks browser events. For this supported advanced pattern, preserve the returned attribution metadata in the rendered DOM as described in [Backend-Fetched Recommendations and SDK Event Tracking](https://docs.froomle.com/froomle-doc-main/v2/js_lib/jslib.html#HybridBackendTracking).

<a id="_failure_handling"></a>

## Failure handling

Set an application-level timeout and ensure recommendation failures do not block the main page or application flow. Handle empty responses and request failures using the agreed fallback strategy. See [Resilience and fallbacks](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/resilience-and-fallbacks.html) for production patterns.

<a id="_related_topics"></a>

## Related topics

-   [Manual event integration](https://docs.froomle.com/froomle-doc-main/v2/guide/manual-integration/events.html)
    
-   [Modules and placements](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/modules-and-placements.html)
    
-   [Filters and constraints](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/filters-and-constraints.html)
    
-   [QA and monitoring](https://docs.froomle.com/froomle-doc-main/v2/guide/topics/qa-monitoring.html)
