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

# Custom Properties

When building AI applications, you often need to track and analyze requests by different dimensions like project, feature, or workflow stage. Custom Properties let you tag LLM requests with metadata, enabling advanced filtering, cost analysis per user or feature, and performance tracking across different parts of your application.

<Frame caption="Custom Properties appear as headers in the `Request` table. ">
  <img src="https://mintcdn.com/helicone/tEQUFyBH7IjDxuEd/images/example-custom-properties.png?fit=max&auto=format&n=tEQUFyBH7IjDxuEd&q=85&s=be009949f6ff8cfef86ad27e12db83a1" alt="Helicone Custom Properties feature for filtering and segmenting data in the Request table." width="1440" height="796" data-path="images/example-custom-properties.png" />
</Frame>

## Why use Custom Properties

* **Track unit economics**: Calculate cost per user, conversation, or feature to understand your application's profitability
* **Debug complex workflows**: Group related requests in multi-step AI processes for easier troubleshooting
* **Analyze performance by segment**: Compare latency and costs across different user types, features, or environments

## Quick Start

Use headers to add Custom Properties to your LLM requests.

<Steps>
  <Step title="Define the Header">
    Name your header in the format `Helicone-Property-[Name]` where `Name` is the name of your custom property.
  </Step>

  <Step title="Define the Value">
    The value is a string that labels your request for this custom property. Here are some examples:

    <CodeGroup>
      ```js Node.js theme={null}
      import { OpenAI } from "openai";

      const client = new OpenAI({
        baseURL: "https://ai-gateway.helicone.ai",
        apiKey: process.env.HELICONE_API_KEY,
        defaultHeaders: {
          "Helicone-Property-Conversation": "support_issue_2",
          "Helicone-Property-App": "mobile",
          "Helicone-Property-Environment": "production",
        },
      });

      const response = await client.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [{ role: "user", content: "Hello, how are you?" }]
      });
      ```

      ```python Python theme={null}
      from openai import OpenAI

      client = OpenAI(
          base_url="https://ai-gateway.helicone.ai",
          api_key=os.getenv("HELICONE_API_KEY"),
          default_headers={
              "Helicone-Property-Conversation": "support_issue_2",
              "Helicone-Property-App": "mobile",
              "Helicone-Property-Environment": "production",
          }
      )

      response = client.chat.completions.create(
          model="gpt-4o-mini",
          messages=[{"role": "user", "content": "Hello, how are you?"}]
      )
      ```

      ```bash cURL theme={null}
      curl https://ai-gateway.helicone.ai/chat/completions \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $HELICONE_API_KEY" \
        -H "Helicone-Property-Conversation: support_issue_2" \
        -H "Helicone-Property-App: mobile" \
        -H "Helicone-Property-Environment: production" \
        -d '{
          "model": "gpt-4o-mini",
          "messages": [
            {
              "role": "user",
              "content": "Hello, how are you?"
            }
          ]
        }'
      ```

      ```python Langchain (Python) theme={null}
      from langchain_openai import ChatOpenAI

      llm = ChatOpenAI(
          openai_api_key="<HELICONE_API_KEY>",
          openai_api_base="https://ai-gateway.helicone.ai",
          model_name="gpt-4o-mini",
          default_headers={
              "Helicone-Property-Type": "Course Outline"
          }
      )

      course = llm.predict("Generate a course outline about AI.")

      # Update helicone properties/headers for each request
      llm.model_kwargs["headers"] = {
          "Helicone-Property-Type": "Lesson"
      }

      lesson = llm.predict("Generate a lesson for the AI course.")
      ```
    </CodeGroup>
  </Step>
</Steps>

## Understanding Custom Properties

### How Properties Work

Custom properties are metadata attached to each request that help you:

**What they enable:**

* Filter requests in the dashboard by any property
* Calculate costs and metrics grouped by properties
* Export data segmented by custom dimensions
* Set up alerts based on property values

## Use Cases

<Tabs>
  <Tab title="Environment & Deployment Tracking">
    Track performance and costs across different environments and deployments:

    <CodeGroup>
      ```typescript Node.js theme={null}
      import { OpenAI } from "openai";

      const client = new OpenAI({
        baseURL: "https://ai-gateway.helicone.ai",
        apiKey: process.env.HELICONE_API_KEY,
      });

      // Production deployment
      const response = await client.chat.completions.create(
        {
          model: "gpt-4o-mini",
          messages: [{ role: "user", content: "Process this customer request" }]
        },
        {
          headers: {
            "Helicone-Property-Environment": "production",
            "Helicone-Property-Version": "v2.1.0",
            "Helicone-Property-Region": "us-east-1"
          }
        }
      );

      // Staging deployment with different version
      const testResponse = await client.chat.completions.create(
        {
          model: "gpt-4o-mini", 
          messages: [{ role: "user", content: "Test new feature" }]
        },
        {
          headers: {
            "Helicone-Property-Environment": "staging",
            "Helicone-Property-Version": "v2.2.0-beta",
            "Helicone-Property-Region": "us-west-2"
          }
        }
      );

      // Compare performance and costs across environments
      ```

      ```python Python theme={null}
      from openai import OpenAI
      import os

      client = OpenAI(
          base_url="https://ai-gateway.helicone.ai",
          api_key=os.environ.get("HELICONE_API_KEY"),
      )

      # Production request
      response = client.chat.completions.create(
          model="gpt-4o-mini",
          messages=[{"role": "user", "content": "Process this customer request"}],
          extra_headers={
              "Helicone-Property-Environment": "production",
              "Helicone-Property-Version": "v2.1.0", 
              "Helicone-Property-Region": "us-east-1"
          }
      )

      # Development request 
      dev_response = client.chat.completions.create(
          model="gpt-4o-mini",
          messages=[{"role": "user", "content": "Test prompt changes"}],
          extra_headers={
              "Helicone-Property-Environment": "development",
              "Helicone-Property-Version": "v2.2.0-dev",
              "Helicone-Property-Region": "local"
          }
      )
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Customer Support Bot">
    Track support interactions by ticket ID and case details for debugging and cost analysis:

    ```typescript theme={null}
    // Initial customer inquiry
    const response = await client.chat.completions.create(
      {
        model: "gpt-4o-mini",
        messages: [
          { role: "system", content: "You are a helpful customer support agent." },
          { role: "user", content: "My order hasn't arrived yet, what should I do?" }
        ]
      },
      {
        headers: {
          "Helicone-Property-TicketId": "TICKET-12345",
          "Helicone-Property-Category": "shipping",
          "Helicone-Property-Priority": "medium",
          "Helicone-Property-Channel": "chat"
        }
      }
    );

    // Follow-up question in same ticket
    const followUp = await client.chat.completions.create(
      {
        model: "gpt-4o-mini", 
        messages: [
          { role: "system", content: "You are a helpful customer support agent." },
          { role: "user", content: "Can you help me track the package?" }
        ]
      },
      {
        headers: {
          "Helicone-Property-TicketId": "TICKET-12345",
          "Helicone-Property-Category": "shipping", 
          "Helicone-Property-Priority": "high",  // Escalated priority
          "Helicone-Property-Channel": "chat"
        }
      }
    );

    // Track costs per ticket, debug issues by category, analyze resolution patterns
    ```
  </Tab>
</Tabs>

## Configuration Reference

### Header Format

Custom properties use a simple header-based format:

<ParamField header="Helicone-Property-[Name]" type="string">
  Any custom metadata you want to track. Replace `[Name]` with your property name.

  Example: `Helicone-Property-Environment: staging`
</ParamField>

<ParamField header="Helicone-User-Id" type="string">
  Special reserved property for user tracking. Enables per-user cost analytics and usage metrics. See [User Metrics](/observability/user-metrics) for detailed tracking capabilities.

  Example: `Helicone-User-Id: user-123`
</ParamField>

## Advanced Features

### Updating Properties After Request

You can update properties after a request is made using the [REST API](/rest/request/put-v1request-property):

```typescript theme={null}
// Get the request ID from the response
const { data, response } = await client.chat.completions
  .create({ /* your request */ })
  .withResponse();

const requestId = response.headers.get("helicone-id");

// Update properties via API
await fetch(`https://api.helicone.ai/v1/request/${requestId}/property`, {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${HELICONE_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "Environment": "production",
    "PostProcessed": "true"
  })
});
```

## Querying by Custom Properties

Once you've added custom properties to your requests, you can filter and retrieve requests using those properties via the [Query API](/rest/request/post-v1requestquery-clickhouse).

<Warning>
  **Important:** When filtering by custom properties, you MUST wrap the `properties` filter inside a `request_response_rmt` object. Omitting this wrapper will return empty results.
</Warning>

### Simple Property Filter

Filter requests by a single property value:

```bash theme={null}
curl --request POST \
  --url https://api.helicone.ai/v1/request/query-clickhouse \
  --header "Content-Type: application/json" \
  --header "authorization: Bearer $HELICONE_API_KEY" \
  --data '{
  "filter": {
    "request_response_rmt": {
      "properties": {
        "Environment": {
          "equals": "production"
        }
      }
    }
  },
  "limit": 100
}'
```

### Multiple Property Filters

Combine multiple property filters using AND/OR operators:

```bash theme={null}
curl --request POST \
  --url https://api.helicone.ai/v1/request/query-clickhouse \
  --header "Content-Type: application/json" \
  --header "authorization: Bearer $HELICONE_API_KEY" \
  --data '{
  "filter": {
    "left": {
      "request_response_rmt": {
        "properties": {
          "Environment": {
            "equals": "production"
          }
        }
      }
    },
    "operator": "and",
    "right": {
      "request_response_rmt": {
        "properties": {
          "App": {
            "equals": "mobile"
          }
        }
      }
    }
  },
  "limit": 100
}'
```

### Combining Properties with Other Filters

Filter by properties AND other criteria like date range or model:

```bash theme={null}
curl --request POST \
  --url https://api.helicone.ai/v1/request/query-clickhouse \
  --header "Content-Type: application/json" \
  --header "authorization: Bearer $HELICONE_API_KEY" \
  --data '{
  "filter": {
    "left": {
      "request_response_rmt": {
        "request_created_at": {
          "gte": "2024-01-01T00:00:00Z"
        }
      }
    },
    "operator": "and",
    "right": {
      "request_response_rmt": {
        "properties": {
          "Conversation": {
            "equals": "support_issue_2"
          }
        }
      }
    }
  },
  "limit": 100
}'
```

### Common Mistake

<Accordion title="❌ WRONG - Properties without request_response_rmt wrapper">
  ```bash theme={null}
  # This will return empty results even if data exists
  curl --request POST \
    --url https://api.helicone.ai/v1/request/query-clickhouse \
    --header "Content-Type: application/json" \
    --header "authorization: Bearer $HELICONE_API_KEY" \
    --data '{
    "filter": {
      "properties": {
        "Environment": {
          "equals": "production"
        }
      }
    }
  }'
  ```
</Accordion>

<Accordion title="✅ CORRECT - Properties with request_response_rmt wrapper">
  ```bash theme={null}
  # This will correctly return filtered results
  curl --request POST \
    --url https://api.helicone.ai/v1/request/query-clickhouse \
    --header "Content-Type: application/json" \
    --header "authorization: Bearer $HELICONE_API_KEY" \
    --data '{
    "filter": {
      "request_response_rmt": {
        "properties": {
          "Environment": {
            "equals": "production"
          }
        }
      }
    }
  }'
  ```
</Accordion>

See the [full Query API documentation](/rest/request/post-v1requestquery-clickhouse) for more advanced filtering options.

## Related Features

<CardGroup cols={2}>
  <Card title="User Metrics" icon="chart-line" href="/features/advanced-usage/user-metrics">
    Track per-user costs and usage with the special Helicone-User-Id property
  </Card>

  <Card title="Sessions" icon="link" href="/features/sessions">
    Group related requests with Helicone-Session-Id for workflow tracking
  </Card>

  <Card title="Webhooks" icon="webhook" href="/features/webhooks">
    Filter webhook deliveries based on custom property values
  </Card>

  <Card title="Alerts" icon="bell" href="/features/alerts">
    Set up alerts triggered by specific property combinations
  </Card>
</CardGroup>

***

<Accordion title="Need more help?">
  Additional questions or feedback? Reach out to
  [help@helicone.ai](mailto:help@helicone.ai) or [schedule a
  call](https://cal.com/team/helicone/helicone-discovery) with us.
</Accordion>
