---
title: Create Customization
description: Create a new customization with a defined scope and pins list.
slug: recommendations/customization/create-customization
docKind: endpoint
hub: recommendations
tableOfContents: true
---
import ApiSection from "../../../../components/ApiSection.astro";
import ApiEndpoint from "../../../../components/ApiEndpoint.astro";
import ApiCodeTabs from "../../../../components/ApiCodeTabs.astro";
import { Aside } from "@astrojs/starlight/components";

<ApiSection>
  <div slot="code">
    <ApiEndpoint method="POST" url="https://live.luigisbox.com/v1/recommender/pin/{TRACKER_ID}/scope" />
  </div>
## Overview

Create a new recommendation customization with a target scope and list of pin definitions.

<Aside type="caution">
  This endpoint requires HMAC authentication. See [Authentication](/platform-foundations/api-principles/#authentication).
</Aside>
</ApiSection>

<ApiSection>
## Request parameters

### Path parameters

| Parameter | Type | Required | Description |
| :-- | :-- | :-- | :-- |
| `TRACKER_ID` | string | ✓ | Your unique tracker identifier. |

### Request headers

| Header | Value | Description |
| :-- | :-- | :-- |
| `Content-Type` | `application/json; charset=utf-8` | Required. |
| `Date` | HTTP date | Required for HMAC. |
| `Authorization` | `ApiAuth {TRACKER_ID}:{SIGNATURE}` | Required for HMAC. |

### Request body attributes

| Parameter | Type | Required | Description |
| :-- | :-- | :-- | :-- |
| `model` | string | ✓ | Recommender model, for example `basket`. |
| `target_type` | string | ✓ | Scope type: `item`, `criteria`, or `all`. |
| `target_identity` | string | Cond. | Target item ID when `target_type` is `item`. |
| `target_criteria` | object | Cond. | Criteria definition when `target_type` is `criteria`. |
| `tags` | array |  | Organizational tags for the scope. |
| `target_metadata` | object | Cond. | Optional item metadata, commonly title or image data. |
| `pin_definitions` | array | ✓ | List of pin objects to apply. |

### Pin object attributes

| Parameter | Type | Required | Description |
| :-- | :-- | :-- | :-- |
| `position` | integer | ✓ | Pin position. Use `-1` for global and `0` for block. |
| `pin_type` | string | ✓ | `item` or `criteria`. |
| `pin_identity` | string | Cond. | Item ID when `pin_type` is `item`. |
| `pin_criteria` | object | Cond. | Criteria definition when `pin_type` is `criteria`. |
| `widget_id` | string |  | Widget identifier for the pin. |
| `is_block_pin` | boolean |  | Excludes matching items when `true`. |
| `is_bundle_pin` | boolean |  | Marks the pin as a bundle pin. |
| `active_from` | date-time |  | ISO 8601 start time. |
| `active_to` | date-time |  | ISO 8601 end time. |

  <div slot="code">
    <h4 class="code-section-title">Example Request</h4>

```shell
tracker_id="1234-5678"
private_key="your_private_api_key"
host="https://live.luigisbox.com"
path="/v1/recommender/pin/${tracker_id}/scope"
method="POST"
content_type="application/json; charset=utf-8"
date=$(date -u "+%a, %d %b %Y %H:%M:%S GMT")

string_to_sign="$(printf "${method}\n${content_type}\n${date}\n${path}")"
signature=$(echo -n "${string_to_sign}" | openssl dgst -sha256 -hmac "${private_key}" -binary | base64)

curl -X ${method} "${host}${path}" \
  -H "Content-Type: ${content_type}" \
  -H "Date: ${date}" \
  -H "Authorization: ApiAuth ${tracker_id}:${signature}" \
  -d '{
    "model": "basket",
    "target_type": "item",
    "target_identity": "/p/123",
    "pin_definitions": []
  }'
```
  </div>
</ApiSection>

<ApiSection>
## How to Make a Request

1. Build the HMAC string from the method, content type, date, and request path.
2. Generate the Base64-encoded HMAC SHA-256 signature with your private key.
3. Send the JSON body together with `Date`, `Content-Type`, and `Authorization` headers.

<div slot="code">
  <h4 class="code-section-title">Code Examples</h4>
  <ApiCodeTabs syncKey="recommendations-customization-create">
    <div slot="ruby">

```ruby
require 'faraday'
require 'base64'
require 'openssl'
require 'json'
require 'time'

def generate_luigisbox_digest(private_key, http_method, endpoint_path, date_header, content_type_header)
  data = "#{http_method}\n#{content_type_header}\n#{date_header}\n#{endpoint_path}"
  Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', private_key, data)).strip
end

tracker_id = 'YOUR_TRACKER_ID'
endpoint_path = "/v1/recommender/pin/#{tracker_id}/scope"
content_type = 'application/json; charset=utf-8'
date_header = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S GMT")
signature = generate_luigisbox_digest('your_private_api_key', 'POST', endpoint_path, date_header, content_type)

customization_data = {
  model: 'basket',
  target_type: 'item',
  target_identity: '/p/123',
  pin_definitions: []
}

response = Faraday.new(url: 'https://live.luigisbox.com').post(endpoint_path) do |req|
  req.headers['Date'] = date_header
  req.headers['Content-Type'] = content_type
  req.headers['Authorization'] = "ApiAuth #{tracker_id}:#{signature}"
  req.body = customization_data.to_json
end

puts response.body
```
    </div>
    <div slot="php">

```php
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

function generateLuigisboxDigest($privateKey, $httpMethod, $endpointPath, $dateHeader, $contentTypeHeader) {
    $data = "{$httpMethod}\n{$contentTypeHeader}\n{$dateHeader}\n{$endpointPath}";
    return trim(base64_encode(hash_hmac('sha256', $data, $privateKey, true)));
}

$trackerId = 'YOUR_TRACKER_ID';
$endpointPath = "/v1/recommender/pin/{$trackerId}/scope";
$contentType = 'application/json; charset=utf-8';
$date = gmdate('D, d M Y H:i:s') . ' GMT';
$signature = generateLuigisboxDigest('your_private_api_key', 'POST', $endpointPath, $date, $contentType);

$client = new Client();
$response = $client->request('POST', "https://live.luigisbox.com{$endpointPath}", [
    'headers' => [
        'Content-Type' => $contentType,
        'Date' => $date,
        'Authorization' => "ApiAuth {$trackerId}:{$signature}",
    ],
    'json' => [
        'model' => 'basket',
        'target_type' => 'item',
        'target_identity' => '/p/123',
        'pin_definitions' => [],
    ],
]);

echo $response->getBody();
```
    </div>
    <div slot="javascript">

```javascript
const axios = require('axios');
const crypto = require('crypto');

function generateLuigisBoxDigest(privateKey, httpMethod, endpointPath, dateHeader, contentTypeHeader) {
  const data = `${httpMethod}\n${contentTypeHeader}\n${dateHeader}\n${endpointPath}`;
  return crypto.createHmac('sha256', privateKey).update(data).digest('base64').trim();
}

const trackerId = 'YOUR_TRACKER_ID';
const endpointPath = `/v1/recommender/pin/${trackerId}/scope`;
const contentType = 'application/json; charset=utf-8';
const dateHeader = new Date().toUTCString();
const signature = generateLuigisBoxDigest('your_private_api_key', 'POST', endpointPath, dateHeader, contentType);

axios({
  method: 'POST',
  url: `https://live.luigisbox.com${endpointPath}`,
  headers: {
    'Content-Type': contentType,
    Date: dateHeader,
    Authorization: `ApiAuth ${trackerId}:${signature}`,
  },
  data: {
    model: 'basket',
    target_type: 'item',
    target_identity: '/p/123',
    pin_definitions: [],
  },
}).then((response) => console.log(response.data));
```
    </div>
  </ApiCodeTabs>
</div>
</ApiSection>

<ApiSection>
## Response structure

The response returns the created customization object with its generated ID.

### Response attributes

| Field | Type | Description |
| :-- | :-- | :-- |
| `id` | string | Unique identifier of the customization. |
| `model` | string | Recommender model. |
| `target_type` | string | Target scope type. |
| `created_at` | string | Creation timestamp. |

  <div slot="code">
    <h4 class="code-section-title">Example Response</h4>

```json
{
  "id": "199620b4-df1a-46e8-ab59-9e9ed9af7106",
  "model": "basket",
  "target_type": "item",
  "target_identity": "/p/123",
  "created_at": "2025-01-20T12:00:00Z"
}
```
  </div>
</ApiSection>

<ApiSection>
## Error handling

| HTTP Status | Description |
| :-- | :-- |
| `201 Created` | Customization created successfully. |
| `400 Bad Request` | Invalid configuration provided. |
| `401 Unauthorized` | Missing or invalid authentication. |
| `404 Not Found` | Tracker not found. |
| `422 Unprocessable Entity` | Invalid pin definition provided. |

  <div slot="code">
    <h4 class="code-section-title">Example Error</h4>

```json
{
  "reason": "Invalid pin definition provided",
  "exception_details": {
    "pin_definitions": ["Missing required field: position"],
    "target_type": ["Must be one of: item, criteria, all"]
  }
}
```
  </div>
</ApiSection>
