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

# Text Search: Submit Feedback

> Search Feedback API endpoint to submit qualitative shopper feedback — overall thumbs up/down, free-form text, and per-product ratings — captured against a specific search.

This endpoint allows you to capture user feedback on search results, including overall ratings, text feedback, and product-specific feedback. When feedback is submitted, the endpoint retrieves the cached search data (if still available) and stores the original search query, expanded queries, and intent modifier actions alongside the feedback for analysis.

## Authorization

<ParamField header="X-Storefront-Access-Token" type="string" required>
  Token-based authentication header in the form of `<YOUR_LAYERS_TOKEN>`. Same requirements as the Search API.
</ParamField>

## Headers

<ParamField header="Content-Type" type="string" default="application/json" required />

<ParamField header="Accept" type="string" default="application/json" required />

## Body

<ParamField body="search_id" type="string" required>
  ULID identifier of the search to provide feedback on. This should be the `search_id` returned from the [Prepare Search](/api-reference/search-prepare) endpoint.
</ParamField>

<ParamField body="rating" type="string">
  Overall rating for the search results. Must be one of:

  * `"positive"` - Thumbs up, results were helpful
  * `"negative"` - Thumbs down, results were not helpful
</ParamField>

<ParamField body="text_feedback" type="string">
  Free-form text feedback from the user. Maximum 2000 characters.
</ParamField>

<ParamField body="product_feedback" type="array">
  Array of product-specific feedback objects. Each object contains:

  <Expandable title="Properties">
    <ParamField body="product_id" type="integer" required>
      The product ID to provide feedback on.
    </ParamField>

    <ParamField body="rating" type="string">
      Rating for this specific product. Must be `"positive"` or `"negative"`.
    </ParamField>

    <ParamField body="feedback" type="string">
      Optional text feedback for this product. Maximum 500 characters.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="identity" type="object">
  User identity information for tracking and personalization. Automatically managed by the Storefront Pixel; required for headless integrations.

  <Expandable title="Properties">
    <ParamField body="deviceId" type="string">
      Persistent browser identifier that remains constant across sessions. Used to recognize returning visitors for long-term personalization. Automatically generated and managed by the Storefront Pixel.
    </ParamField>

    <ParamField body="sessionId" type="string">
      Temporary session identifier. Typically corresponds to the Shopify session ID and expires after inactivity or when the browser is closed.
    </ParamField>

    <ParamField body="customerId" type="string">
      Shopify customer ID for authenticated users. Only present when the user is signed in to their account.
    </ParamField>

    <ParamField body="companyLocationId" type="string">
      Shopify B2B company location GID (e.g. `gid://shopify/CompanyLocation/123456789`) for the authenticated buyer. Layers resolves the catalog assigned to the company location and uses it to scope results: only products in the catalog are returned, prices come from the catalog's price list, and excluded collections return 404. Omit for retail (DTC) traffic. See [B2B catalogs](/shopify-integration/b2b-catalogs).
    </ParamField>
  </Expandable>
</ParamField>

## Validation rules

* `search_id` is required and must be a valid ULID
* `rating` must be either `"positive"` or `"negative"` if provided
* `text_feedback` has a maximum length of 2000 characters
* `product_feedback` is an array where each item must include:
  * `product_id` (required when `product_feedback` is provided)
  * `rating` must be `"positive"` or `"negative"` if provided
  * `feedback` has a maximum length of 500 characters
* At least one of `rating`, `text_feedback`, or `product_feedback` should be provided, though all fields are technically optional

## Behavior

* Returns HTTP 201 (Created) when feedback is successfully recorded
* Attempts to retrieve cached search data using the provided `search_id`
* If cached data is available (within 15 minutes of the original search), captures:
  * Original search query
  * Expanded queries used for search
  * Intent modifier actions applied
* If cached data is not available (expired or invalid `search_id`), feedback is still recorded but without the search context
* Stores identity information (`sessionId` and `customerId`) if provided

## Response

### 201 Created

<ResponseField name="id" type="string">
  ULID identifier for the created feedback record.
</ResponseField>

<ResponseField name="search_id" type="string">
  The search ID that was provided in the request.
</ResponseField>

<ResponseField name="recorded" type="boolean">
  Always `true` when feedback is successfully recorded.
</ResponseField>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "01HZY7K2M8N9P4Q5R6S7T8U9V0",
    "search_id": "01HZY7J9ZV6K2T3M6P4FJ3F2QG",
    "recorded": true
  }
  ```
</ResponseExample>

### Error conditions

* 401 Unauthorized: Missing or invalid `X-Storefront-Access-Token`
* 422 Unprocessable Entity: Invalid body or validation errors

## Example usage

### Basic rating feedback

```typescript theme={null}
const response = await fetch('https://app.uselayers.com/api/storefront/v1/search/feedback', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Storefront-Access-Token': 'your-token-here'
  },
  body: JSON.stringify({
    search_id: '01HZY7J9ZV6K2T3M6P4FJ3F2QG',
    rating: 'positive'
  })
});

const data = await response.json();
console.log(data);
// { id: "01HZY7K2M8N9P4Q5R6S7T8U9V0", search_id: "01HZY7J9ZV6K2T3M6P4FJ3F2QG", recorded: true }
```

### Feedback with text and identity

```typescript theme={null}
const response = await fetch('https://app.uselayers.com/api/storefront/v1/search/feedback', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Storefront-Access-Token': 'your-token-here'
  },
  body: JSON.stringify({
    search_id: '01HZY7J9ZV6K2T3M6P4FJ3F2QG',
    rating: 'negative',
    text_feedback: 'The results were not relevant to my search.',
    identity: {
      sessionId: 'session-123',
      customerId: 'customer-456'
    }
  })
});

const data = await response.json();
```

### Product-specific feedback

```typescript theme={null}
const response = await fetch('https://app.uselayers.com/api/storefront/v1/search/feedback', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Storefront-Access-Token': 'your-token-here'
  },
  body: JSON.stringify({
    search_id: '01HZY7J9ZV6K2T3M6P4FJ3F2QG',
    product_feedback: [
      {
        product_id: 123,
        rating: 'positive'
      },
      {
        product_id: 456,
        rating: 'negative',
        feedback: 'Not what I was looking for'
      }
    ]
  })
});

const data = await response.json();
```

## Next steps

* Use this endpoint in conjunction with the [Prepare Search](/api-reference/search-prepare) and [Search](/api-reference/search) endpoints to create a complete search experience with feedback collection
* Analyze collected feedback to improve search relevance and user experience
