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

# Batch Silver

> POST /batch/silver — normalize a selection or entire catalog through the Silver pipeline stage.

## Endpoint

```
POST /api/workspace/{workspaceId}/catalogs/{catalogId}/batch/silver
```

Triggers the Silver normalization stage for a selection of products or the entire catalog. Silver normalizes field values, maps source fields to the Alana schema, validates image URLs, and detects duplicate products.

***

## Path parameters

| Parameter     | Type   | Required | Description            |
| ------------- | ------ | :------: | ---------------------- |
| `workspaceId` | string |    Yes   | Your workspace ID      |
| `catalogId`   | string |    Yes   | The catalog to process |

***

## Request body

```json theme={null}
{
  "productIds": ["prod_abc", "prod_def"],
  "scope": "selection"
}
```

| Field        | Type      | Required | Description                                                     |
| ------------ | --------- | :------: | --------------------------------------------------------------- |
| `productIds` | string\[] |    No    | Product IDs to process. Required when `scope` is `"selection"`  |
| `scope`      | string    |    Yes   | `"selection"` or `"all"`. When `"all"`, `productIds` is ignored |

***

## Response

```json theme={null}
{
  "results": [
    {
      "productId": "prod_abc",
      "status": "success",
      "fieldsNormalized": 4,
      "duplicateOf": null,
      "urlsValidated": 3
    },
    {
      "productId": "prod_def",
      "status": "success",
      "fieldsNormalized": 2,
      "duplicateOf": "prod_xyz",
      "urlsValidated": 1
    },
    {
      "productId": "prod_ghi",
      "status": "error",
      "error": "Brand not found: 'AcmeCorp'"
    }
  ],
  "processed": 3,
  "duration_ms": 1240
}
```

### SilverResult fields

| Field              | Type           | Description                                                  |
| ------------------ | -------------- | ------------------------------------------------------------ |
| `productId`        | string         | The product that was processed                               |
| `status`           | string         | `"success"` or `"error"`                                     |
| `fieldsNormalized` | number         | Count of fields that were transformed                        |
| `duplicateOf`      | string \| null | If a duplicate was detected, the ID of the canonical product |
| `urlsValidated`    | number         | Count of image/media URLs checked for HTTP 200               |
| `error`            | string         | Present only when `status` is `"error"`                      |

### Top-level response fields

| Field         | Type            | Description                                |
| ------------- | --------------- | ------------------------------------------ |
| `results`     | SilverResult\[] | Per-product results                        |
| `processed`   | number          | Total products processed (success + error) |
| `duration_ms` | number          | Total processing time in milliseconds      |

***

## Examples

<CodeGroup>
  ```bash curl theme={null}
  # Normalize a selection of products
  curl -X POST "https://app.alana.shopping/api/workspace/ws_123/catalogs/cat_456/batch/silver" \
    -H "Authorization: Bearer sk_live_your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "productIds": ["prod_abc", "prod_def", "prod_ghi"],
      "scope": "selection"
    }'
  ```

  ```javascript JavaScript theme={null}
  const workspaceId = 'ws_123';
  const catalogId = 'cat_456';

  const response = await fetch(
    `https://app.alana.shopping/api/workspace/${workspaceId}/catalogs/${catalogId}/batch/silver`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.ALANA_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        productIds: ['prod_abc', 'prod_def', 'prod_ghi'],
        scope: 'selection',
      }),
    }
  );

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error.message);
  }

  const { results, processed, duration_ms } = await response.json();

  console.log(`Processed ${processed} products in ${duration_ms}ms`);

  const duplicates = results.filter(r => r.duplicateOf);
  if (duplicates.length > 0) {
    console.log(`Found ${duplicates.length} duplicates`);
  }
  ```

  ```python Python theme={null}
  import requests
  import os

  workspace_id = "ws_123"
  catalog_id = "cat_456"

  response = requests.post(
      f"https://app.alana.shopping/api/workspace/{workspace_id}/catalogs/{catalog_id}/batch/silver",
      headers={
          "Authorization": f"Bearer {os.environ['ALANA_API_KEY']}",
          "Content-Type": "application/json",
      },
      json={
          "productIds": ["prod_abc", "prod_def", "prod_ghi"],
          "scope": "selection",
      }
  )

  response.raise_for_status()
  data = response.json()

  print(f"Processed {data['processed']} products in {data['duration_ms']}ms")

  errors = [r for r in data["results"] if r["status"] == "error"]
  if errors:
      for e in errors:
          print(f"Error on {e['productId']}: {e['error']}")
  ```
</CodeGroup>

***

## Normalize entire catalog

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://app.alana.shopping/api/workspace/ws_123/catalogs/cat_456/batch/silver" \
    -H "Authorization: Bearer sk_live_your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{"scope": "all"}'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://app.alana.shopping/api/workspace/${workspaceId}/catalogs/${catalogId}/batch/silver`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.ALANA_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ scope: 'all' }),
    }
  );
  const result = await response.json();
  console.log(`Normalized ${result.processed} products`);
  ```

  ```python Python theme={null}
  import requests, os

  response = requests.post(
      f"https://app.alana.shopping/api/workspace/{workspace_id}/catalogs/{catalog_id}/batch/silver",
      headers={"Authorization": f"Bearer {os.environ['ALANA_API_KEY']}"},
      json={"scope": "all"}
  )
  data = response.json()
  print(f"Normalized {data['processed']} products in {data['duration_ms']}ms")
  ```
</CodeGroup>

***

## Error responses

| HTTP status | Code                       | Description                                              |
| ----------- | -------------------------- | -------------------------------------------------------- |
| 400         | `VALIDATION_ERROR`         | `scope` is missing or invalid                            |
| 403         | `INSUFFICIENT_PERMISSIONS` | API key lacks `catalogs:write`                           |
| 404         | `CATALOG_NOT_FOUND`        | Catalog does not exist in workspace                      |
| 409         | `JOB_ALREADY_RUNNING`      | A Silver or Gold job is already in progress              |
| 429         | `RATE_LIMIT_EXCEEDED`      | Slow down and retry after the `Retry-After` header value |
