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

# Making Authenticated Requests

> Authenticate every call with the X-Api-Client-Key header and the right scope.

Once you have an [API key](/authentication/request-api-key), every request to the Enrichment API
carries it in a single header.

## The authentication header

Send your raw key in the `X-Api-Client-Key` header on every request:

```bash theme={null}
curl https://api.verzla.com/api/enrichment/attribute-templates \
  -H "X-Api-Client-Key: sk_live_your_key_here"
```

The server hashes your key (SHA-256), looks up the matching API client, resolves the merchant
organization, and updates the client's `lastUsedAt` timestamp.

<Note>
  The key identifies **both** who you are and which merchant organization you operate on. You never
  pass an organization ID — it is derived from the key.
</Note>

## Scopes

Each endpoint requires a specific scope. Your key is granted one or more:

| Scope            | Required by                                                                               |
| ---------------- | ----------------------------------------------------------------------------------------- |
| `catalog:read`   | `GET /attribute-templates`, `GET /products`, `GET /products/skus`, `GET /acceptance-rate` |
| `catalog:submit` | `POST /products/submissions`                                                              |

## Responses to expect

<ResponseField name="401 Unauthorized" type="error">
  No key was sent, or the key is invalid / inactive. Check the `X-Api-Client-Key` header.
</ResponseField>

<ResponseField name="403 Forbidden" type="error">
  Your key is valid but lacks the scope the endpoint requires. The body names the missing scope,
  e.g. `Missing required scope: catalog:submit`.
</ResponseField>

<ResponseField name="400 Bad Request" type="error">
  The request itself is malformed — too many SKUs, an empty submission, a bad `modifiedSince` value,
  and so on. See each endpoint's reference for specifics.
</ResponseField>

## A reusable client

A thin wrapper keeps the key and base URL in one place:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const BASE = "https://api.verzla.com/api/enrichment";

  function enrichmentFetch(path: string, init: RequestInit = {}) {
    return fetch(`${BASE}${path}`, {
      ...init,
      headers: {
        "X-Api-Client-Key": process.env.VERZLA_ENRICHMENT_KEY!,
        "Content-Type": "application/json",
        ...init.headers,
      },
    });
  }

  // Usage
  const res = await enrichmentFetch("/attribute-templates");
  const templates = await res.json();
  ```

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

  BASE = "https://api.verzla.com/api/enrichment"
  SESSION = requests.Session()
  SESSION.headers.update({
      "X-Api-Client-Key": os.environ["VERZLA_ENRICHMENT_KEY"],
      "Content-Type": "application/json",
  })

  templates = SESSION.get(f"{BASE}/attribute-templates").json()
  ```
</CodeGroup>

<Warning>
  Keep the key server-side. Because it grants read and write access to a merchant's catalog, it must
  never reach a browser, mobile app, or any client you don't fully control.
</Warning>
