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

# Name features, customers and runs

> Tag each LLM call with its feature, customer and call name, and group the calls of one run, through the gateway or with telemetry.

This page follows one example: a support feature that answers a customer's ticket in two LLM calls, first classifying it, then drafting the reply. Each step works the same whether your calls go through the [gateway](/api/gateway/responses) or straight to your provider, recorded by [telemetry](/sdks/telemetry): pick your tab.

## 1. Set up

<Tabs>
  <Tab title="Gateway">
    Turn on the `x-weflayr-metadata` header once: every gateway call, made by any OpenAI client, then carries the values of the scope it is made in.

    <CodeGroup>
      ```python Python theme={null}
      import weflayr
      from openai import OpenAI

      weflayr.enable_propagate_metadata_to_gateway()

      client = OpenAI(base_url="https://gateway.weflayr.com/v1", api_key="<WEFLAYR_API_KEY>")
      ```

      ```js Node.js theme={null}
      const weflayr = require('weflayr');
      const OpenAI = require('openai');

      weflayr.setup(); // first: it lets the values follow your code into the OpenAI call
      weflayr.enablePropagateMetadataToGateway();

      const client = new OpenAI({
        baseURL: 'https://gateway.weflayr.com/v1',
        apiKey: process.env.WEFLAYR_API_KEY,
      });
      ```
    </CodeGroup>

    Without the SDK, set the same values on each request yourself: see the [gateway's Weflayr fields](/api/gateway/responses#weflayr-fields).
  </Tab>

  <Tab title="Telemetry">
    Instrument your provider's SDK once: every call it makes is then recorded with the values of the scope it is made in. See [Telemetry](/sdks/telemetry) for every provider.

    <CodeGroup>
      ```python Python theme={null}
      import weflayr
      from openai import OpenAI

      weflayr.auto_instrument(ai_sdks=weflayr.AiSdk.OPENAI)

      client = OpenAI()
      ```

      ```js Node.js theme={null}
      const weflayr = require('weflayr');
      const OpenAI = require('openai');

      await weflayr.autoInstrument({ aiSdks: 'openai' });

      const client = new OpenAI();
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## 2. Tag the feature run and name its calls

Wrap the feature in `propagate_metadata` / `propagateMetadata`: every LLM call made inside, including nested ones, carries its values, and the scope is one **feature run**, which [AI cost](/product/ai-cost) counts and prices. Inside it, `set_call_name` / `setCallName` names each call.

* `feature_name`: the product feature making the calls.
* `customer_name`: who the calls are for, to join your AI cost with your [revenue](/product/sync-revenue).
* `call_name`: one LLM call of the feature. A [benchmark](/product/model-benchmarks) is set up per call name.

<Tabs>
  <Tab title="Gateway">
    <CodeGroup>
      ```python Python theme={null}
      def answer_ticket(ticket, customer):
          with weflayr.propagate_metadata(
              feature_name="support-ticket",
              customer_name=customer,
          ):
              with weflayr.set_call_name("classify_ticket"):
                  category = client.responses.create(
                      model="claude-opus-5-5",
                      input=f"Classify: {ticket}",
                  ).output_text
              with weflayr.set_call_name("draft_reply"):
                  return client.responses.create(
                      model="claude-opus-5-5",
                      input=f"Reply to this {category} ticket: {ticket}",
                  ).output_text
      ```

      ```js Node.js theme={null}
      function answerTicket(ticket, customer) {
        return weflayr.propagateMetadata(
          { featureName: 'support-ticket', customerName: customer },
          async () => {
            const category = await weflayr.setCallName('classify_ticket', async () => {
              const response = await client.responses.create({
                model: 'claude-opus-5-5',
                input: `Classify: ${ticket}`,
              });
              return response.output_text;
            });
            return weflayr.setCallName('draft_reply', async () => {
              const response = await client.responses.create({
                model: 'claude-opus-5-5',
                input: `Reply to this ${category} ticket: ${ticket}`,
              });
              return response.output_text;
            });
          },
        );
      }
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Telemetry">
    <CodeGroup>
      ```python Python theme={null}
      def answer_ticket(ticket, customer):
          with weflayr.propagate_metadata(
              feature_name="support-ticket",
              customer_name=customer,
          ):
              with weflayr.set_call_name("classify_ticket"):
                  category = client.responses.create(
                      model="gpt-5.6",
                      input=f"Classify: {ticket}",
                  ).output_text
              with weflayr.set_call_name("draft_reply"):
                  return client.responses.create(
                      model="gpt-5.6",
                      input=f"Reply to this {category} ticket: {ticket}",
                  ).output_text
      ```

      ```js Node.js theme={null}
      function answerTicket(ticket, customer) {
        return weflayr.propagateMetadata(
          { featureName: 'support-ticket', customerName: customer },
          async () => {
            const category = await weflayr.setCallName('classify_ticket', async () => {
              const response = await client.responses.create({
                model: 'gpt-5.6',
                input: `Classify: ${ticket}`,
              });
              return response.output_text;
            });
            return weflayr.setCallName('draft_reply', async () => {
              const response = await client.responses.create({
                model: 'gpt-5.6',
                input: `Reply to this ${category} ticket: ${ticket}`,
              });
              return response.output_text;
            });
          },
        );
      }
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## 3. Keep one run across processes

Each scope starts a new feature run. When one run spans several processes, for example the ticket classified by your API and the reply drafted by a worker, the two scopes would count as two runs. Create the run's id once where it starts, send it along with the work, and pass it to every scope of the run.

<Tabs>
  <Tab title="Gateway">
    <CodeGroup>
      ```python Python theme={null}
      # In your API
      feature_run_uuid = weflayr.create_feature_run_uuid()
      with weflayr.propagate_metadata(
          feature_name="support-ticket",
          customer_name=customer,
          feature_run_uuid=feature_run_uuid,
      ):
          with weflayr.set_call_name("classify_ticket"):
              category = client.responses.create(
                  model="claude-opus-5-5",
                  input=f"Classify: {ticket}",
              ).output_text
      queue.send({
          "ticket": ticket,
          "category": category,
          "customer": customer,
          "feature_run_uuid": feature_run_uuid,
      })

      # In your worker, another process
      with weflayr.propagate_metadata(
          feature_name="support-ticket",
          customer_name=job["customer"],
          feature_run_uuid=job["feature_run_uuid"],
      ):
          with weflayr.set_call_name("draft_reply"):
              client.responses.create(
                  model="claude-opus-5-5",
                  input=f"Reply to this {job['category']} ticket: {job['ticket']}",
              )
      ```

      ```js Node.js theme={null}
      // In your API
      const featureRunUuid = weflayr.createFeatureRunUuid();
      const category = await weflayr.propagateMetadata(
        { featureName: 'support-ticket', customerName: customer, featureRunUuid },
        () =>
          weflayr.setCallName('classify_ticket', async () => {
            const response = await client.responses.create({
              model: 'claude-opus-5-5',
              input: `Classify: ${ticket}`,
            });
            return response.output_text;
          }),
      );
      await queue.send({ ticket, category, customer, featureRunUuid });

      // In your worker, another process
      await weflayr.propagateMetadata(
        { featureName: 'support-ticket', customerName: job.customer, featureRunUuid: job.featureRunUuid },
        () =>
          weflayr.setCallName('draft_reply', () =>
            client.responses.create({
              model: 'claude-opus-5-5',
              input: `Reply to this ${job.category} ticket: ${job.ticket}`,
            }),
          ),
      );
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Telemetry">
    <CodeGroup>
      ```python Python theme={null}
      # In your API
      feature_run_uuid = weflayr.create_feature_run_uuid()
      with weflayr.propagate_metadata(
          feature_name="support-ticket",
          customer_name=customer,
          feature_run_uuid=feature_run_uuid,
      ):
          with weflayr.set_call_name("classify_ticket"):
              category = client.responses.create(
                  model="gpt-5.6",
                  input=f"Classify: {ticket}",
              ).output_text
      queue.send({
          "ticket": ticket,
          "category": category,
          "customer": customer,
          "feature_run_uuid": feature_run_uuid,
      })

      # In your worker, another process
      with weflayr.propagate_metadata(
          feature_name="support-ticket",
          customer_name=job["customer"],
          feature_run_uuid=job["feature_run_uuid"],
      ):
          with weflayr.set_call_name("draft_reply"):
              client.responses.create(
                  model="gpt-5.6",
                  input=f"Reply to this {job['category']} ticket: {job['ticket']}",
              )
      ```

      ```js Node.js theme={null}
      // In your API
      const featureRunUuid = weflayr.createFeatureRunUuid();
      const category = await weflayr.propagateMetadata(
        { featureName: 'support-ticket', customerName: customer, featureRunUuid },
        () =>
          weflayr.setCallName('classify_ticket', async () => {
            const response = await client.responses.create({
              model: 'gpt-5.6',
              input: `Classify: ${ticket}`,
            });
            return response.output_text;
          }),
      );
      await queue.send({ ticket, category, customer, featureRunUuid });

      // In your worker, another process
      await weflayr.propagateMetadata(
        { featureName: 'support-ticket', customerName: job.customer, featureRunUuid: job.featureRunUuid },
        () =>
          weflayr.setCallName('draft_reply', () =>
            client.responses.create({
              model: 'gpt-5.6',
              input: `Reply to this ${job.category} ticket: ${job.ticket}`,
            }),
          ),
      );
      ```
    </CodeGroup>
  </Tab>
</Tabs>

Any id you pass must have the shape `create_feature_run_uuid` / `createFeatureRunUuid` gives: 32 lowercase hexadecimal characters.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.