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

# Setup

> Install Langfuse, point it at Lemma, and send your first trace

Lemma uses [Langfuse](https://langfuse.com) as its instrumentation library. You write Langfuse spans; Langfuse exports them to Lemma over OpenTelemetry. This page wires up the exporter once. After this, every page in [Instrumentation](/tracing/instrumentation/traces) is just Langfuse code.

## 1. Install

<Tabs>
  <Tab title="TypeScript">
    ```bash theme={null}
    npm install @langfuse/tracing @langfuse/otel @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-proto
    ```

    Pin the Langfuse v4 SDK (`@langfuse/tracing` + `@langfuse/otel`). The examples in these docs use `startActiveObservation` and `propagateAttributes` from that version.
  </Tab>

  <Tab title="Python">
    ```bash theme={null}
    pip install "langfuse>=3,<4" opentelemetry-sdk opentelemetry-exporter-otlp
    ```

    Pin the Langfuse v3 Python SDK. The examples use `start_as_current_span` / `start_as_current_generation` and `update_current_trace`.
  </Tab>
</Tabs>

## 2. Set environment variables

Find your API key and project ID in [Lemma project settings](https://platform.uselemma.ai).

```bash theme={null}
export LEMMA_BASE_URL="https://api.uselemma.ai/otel/v1/traces"
export LEMMA_API_KEY="lma_..."
export LEMMA_PROJECT_ID="proj_..."
```

<Note>
  Exporting **only** to Lemma does not require `LANGFUSE_*` credentials. You only need a Langfuse account if you also want traces in Langfuse — see [dual export](/tracing/patterns/dual-export).
</Note>

## 3. Register the exporter once at startup

Register this before any agent, provider client, or tool runs. Late initialization is the most common reason spans go missing.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    // instrumentation.ts — imported first, before your app code
    import { LangfuseSpanProcessor } from "@langfuse/otel";
    import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
    import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";

    export const lemmaProcessor = new LangfuseSpanProcessor({
      exporter: new OTLPTraceExporter({
        url: process.env.LEMMA_BASE_URL,
        headers: {
          Authorization: `Bearer ${process.env.LEMMA_API_KEY}`,
          "X-Lemma-Project-ID": process.env.LEMMA_PROJECT_ID,
        },
      }),
    });

    new NodeTracerProvider({ spanProcessors: [lemmaProcessor] }).register();
    ```

    In Next.js, put this in the root `instrumentation.ts` and guard it to the Node.js runtime:

    ```typescript theme={null}
    export async function register() {
      if (process.env.NEXT_RUNTIME === "nodejs") {
        await import("./instrumentation");
      }
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    # instrumentation.py — imported first, before your app code
    import os
    from opentelemetry import trace
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

    provider = TracerProvider()
    provider.add_span_processor(
        BatchSpanProcessor(
            OTLPSpanExporter(
                endpoint=os.environ["LEMMA_BASE_URL"],
                headers={
                    "Authorization": f"Bearer {os.environ['LEMMA_API_KEY']}",
                    "X-Lemma-Project-ID": os.environ["LEMMA_PROJECT_ID"],
                },
            )
        )
    )
    trace.set_tracer_provider(provider)
    ```

    The Langfuse Python SDK attaches to this tracer provider automatically.
  </Tab>
</Tabs>

## 4. Send your first trace

```typescript theme={null}
import { startActiveObservation } from "@langfuse/tracing";

await startActiveObservation("hello-agent", async (root) => {
  root.update({
    input: "What is the capital of France?",
    output: "Paris.",
  });
});
```

Open the [Lemma dashboard](https://platform.uselemma.ai) → **Traces**. Your trace appears within seconds.

## Flushing

Span processors batch and export in the background. In short-lived or serverless runtimes, flush before the process exits so the whole trace ships in one batch.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { lemmaProcessor } from "./instrumentation";

    // at the end of a request / serverless handler
    await lemmaProcessor.forceFlush();
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from langfuse import get_client

    get_client().flush()
    ```
  </Tab>
</Tabs>

<Warning>
  Without a flush, a serverless function can exit before the root span is exported. If the root is dropped, the trace's child spans can be orphaned and the trace discarded. Always flush at the end of short-lived handlers.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Traces" icon="git-branch" href="/tracing/instrumentation/traces">
    Define the root span for one agent execution.
  </Card>

  <Card title="Generations" icon="sparkles" href="/tracing/instrumentation/generations">
    Capture LLM calls with model and token usage.
  </Card>

  <Card title="Tool calls" icon="wrench" href="/tracing/instrumentation/tool-calls">
    Record tool arguments and results as child spans.
  </Card>

  <Card title="Trace contract" icon="file-check" href="/reference/trace-contract">
    The exact shape Lemma reads.
  </Card>
</CardGroup>
