---
title: Claude API에서 OpenTelemetry 사용하기
url: https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api
description: Claude Console에서 대상을 추가하여 Messages API 요청 및 Managed Agents 세션의 OpenTelemetry 트레이스를 관측성 플랫폼으로 전송하세요.
featureMetadata:
  topic:
    title: OpenTelemetry
    url: https://platform.claude.com/docs/en/manage-claude/opentelemetry
  status: beta
  zdr:
    eligibility: eligible
    note: Excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements). Messages API처럼 그 자체로 ZDR 대상인 기능에만 해당합니다. Claude Managed Agents는 ZDR 대상이 아닙니다.
  supportedPlatforms:
    Claude API: beta
    Claude Platform on AWS: beta
  rows:
    - label: 호환 대상
      value: Messages API, Claude Managed Agents
  details:
    - 전달 후 데이터는 Anthropic 외부에서 귀하가 선택한 서비스가 보유하며, 해당 서비스의 보존 및 접근 제어가 적용됩니다.
---

OpenTelemetry를 사용하면 Claude API는 실행하는 각 모델 요청과 각 도구 사용에 대해 *"span"*(스팬)을 내보낼 수 있습니다. 코드를 변경하지 않고도 관측성 플랫폼에서 이러한 스팬을 확인할 수 있습니다.

API는 Messages API 요청과 Managed Agents 세션에 대한 스팬을 생성합니다. 이를 받으려면 Claude Console에서 *"destination"*(대상)을 추가하세요. 대상은 API에 관측성 플랫폼의 OTLP 엔드포인트와 인증 방법을 알려줍니다.

## 필요한 것

* Claude Console 조직의 **관리자 역할**. 대상을 추가하고 변경하려면 이 역할이 필요합니다. 개발자 역할로는 대상을 볼 수 있습니다. 조직이 Claude Platform on AWS를 사용하는 경우, [로그인](https://platform.claude.com/docs/ko/build-with-claude/claude-platform-on-aws#signing-in)에 설명된 대로 AWS Console을 통해 Console에 로그인하세요.
* 포트 443의 HTTPS URL에서 protobuf 인코딩을 허용하는 **OTLP/HTTP 엔드포인트**. 자체 [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/)를 운영하는 경우, 해당 OTLP/HTTP 수신기는 기본적으로 포트 4318에서 수신 대기합니다. 수신기가 포트 443에서 HTTPS를 허용하도록 구성하거나, Collector 앞에 로드 밸런서 또는 리버스 프록시를 두세요.
* API 키와 같은 **엔드포인트 자격 증명**.

## 1. 대상 추가

<Info>
  텔레메트리 대상을 추가하면, Anthropic은 귀하의 제공업체 약관 및 접근 제어에 따라 조직 내 Claude Platform 활동의 트레이스를 귀하가 지정한 서드파티 제공업체의 OTLP/HTTP 엔드포인트로 전송합니다. 전달 후 데이터는 Anthropic 외부에서 귀하가 선택한 서비스가 보유하며, 해당 서비스의 보존 및 접근 제어가 적용됩니다.
</Info>

1. Console에서 조직의 [OpenTelemetry 설정 페이지](https://platform.claude.com/settings/telemetry-destinations)를 열고 **Add destination**을 클릭하세요.

2. 대상을 구성하세요:

   | 필드                             | 입력할 내용                                                                                                                                                                                                                                                                                                  |
   | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | **Provider**                   | 사용하는 관측성 플랫폼, 또는 목록에 없는 경우 **Custom**. 목록에 있는 플랫폼을 선택하면 Console에서 **OTLP endpoint URL**, **Request headers**, **OTLP traces endpoint URL** 대신 API 키와 같은 해당 플랫폼의 세부 정보를 요청합니다.                                                                                                                           |
   | **Name**                       | 선택 사항입니다. 예: "Acme의 관측성 플랫폼."                                                                                                                                                                                                                                                                           |
   | **OTLP endpoint URL**          | IP 주소가 아닌 호스트 이름을 사용하는 관측성 플랫폼 OTLP/HTTP 엔드포인트의 기본 HTTPS URL. 경로 접두사를 포함할 수 있습니다(예: `https://gateway.example.com/otlp`). API가 `/v1/traces`를 추가하므로 이를 직접 추가하지 마세요. **OTLP traces endpoint URL**을 설정한 경우 API는 그 URL을 대신 사용합니다.                                                                            |
   | **Request headers**            | API가 각 요청과 함께 보내는 헤더입니다(예: 관측성 플랫폼의 API 키 헤더). 자격 증명은 헤더 값에만 넣으세요. 저장한 후에는 Console에서 값을 다시 표시하지 않습니다.                                                                                                                                                                                                   |
   | **Content mode**               | **Metadata only** 또는 **Full content**. [콘텐츠 모드의 차이점](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#choose-a-content-mode)을 참조하세요.                                                                                                                                                     |
   | **Workspaces**                 | 대상이 스팬을 받을 워크스페이스: **All**, **Selected** 또는 **All except**. [대상의 워크스페이스 선택](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#choose-workspaces-for-a-destination)을 참조하세요.                                                                                                            |
   | **OTLP traces endpoint URL**   | 선택 사항으로, **Advanced settings** 아래에 있습니다. 관측성 플랫폼 트레이스 엔드포인트의 전체 URL(예: `https://gateway.example.com/custom/traces`). 해당 플랫폼이 OTLP endpoint URL에 `/v1/traces`를 더한 URL이 아닌 다른 URL에서 트레이스를 받는 경우 설정하세요. OTLP endpoint URL과 동일한 호스트여야 합니다.                                                                  |
   | **Maximum content size in MB** | 선택 사항으로, 콘텐츠 모드가 **Full content**인 경우 **Advanced settings** 아래에 있습니다. API가 각 스팬에서 보내는 최대 콘텐츠 크기입니다. 선택한 **Provider**에 따라 Console이 이 값을 채웁니다. 관측성 플랫폼이 허용하는 크기에 맞게 변경하세요. 비워 두면 API가 허용하는 최대 크기까지 보냅니다. [콘텐츠 제한](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#content-limits)을 참조하세요. |

3. **Add destination**을 클릭하여 저장하세요.

4. 대상의 **More actions** 메뉴에서 **Test**를 클릭하세요. 테스트 스팬이 컬렉터로 전송되었는지, 또는 전송이 어떻게 실패했는지 알려 주는 메시지가 표시됩니다([전송 오류](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#delivery-errors) 참조).

5. `anthropic.test` 스팬이 관측성 플랫폼에 도착했는지 확인하세요.

### Admin API 및 CLI

Console 대신 [Admin API](https://platform.claude.com/docs/ko/manage-claude/admin-api)를 통해 대상을 추가할 수도 있습니다. CLI에 관리자로 로그인하거나, `ANTHROPIC_API_KEY`에 [Admin API 키](https://platform.claude.com/docs/ko/manage-claude/admin-api-keys)를 사용하세요. Claude Platform on AWS에서는 대신 `https://aws-external-anthropic.{region}.api.aws`로 요청을 보내고, [요청 보내기](https://platform.claude.com/docs/ko/build-with-claude/claude-platform-on-aws#making-requests)에서와 같이 AWS 자격 증명으로 서명하세요. 이 경우 대상을 관리할 수 있는 사람은 [IAM 정책](https://platform.claude.com/docs/ko/api/claude-platform-on-aws-iam-actions)에 따라 결정됩니다. 요청에는 해당 리전에 있는 AWS 계정의 워크스페이스만 포함할 수 있습니다.

<CodeGroup>
  ```bash CLI
  # Sign in once as an admin, which opens a browser. Leave ANTHROPIC_API_KEY unset, because it overrides the profile.
  ant auth login --profile admin --scope "org:admin"
  ant --profile admin beta:organization:telemetry-destinations create \
    --endpoint-url https://gateway.example.com/otlp \
    --headers '{authorization: "Bearer YOUR_OBSERVABILITY_API_KEY"}' \
    --traces.content-mode metadata_only \
    --workspaces.type all
  ```

  ```bash cURL
  curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/telemetry_destinations" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: telemetry-destinations-2026-08-11" \
    -H "content-type: application/json" \
    -d '{
      "endpoint_url": "https://gateway.example.com/otlp",
      "headers": {"authorization": "Bearer YOUR_OBSERVABILITY_API_KEY"},
      "traces": {"content_mode": "metadata_only"},
      "workspaces": {"type": "all"}
    }'
  ```

  ```python Python
  client = anthropic.Anthropic()

  destination = client.beta.organization.telemetry_destinations.create(
      endpoint_url="https://gateway.example.com/otlp",
      headers={"authorization": "Bearer YOUR_OBSERVABILITY_API_KEY"},
      traces={"content_mode": "metadata_only"},
      workspaces={"type": "all"},
  )

  print(destination.id)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const destination = await client.beta.organization.telemetryDestinations.create({
    endpoint_url: "https://gateway.example.com/otlp",
    headers: { authorization: "Bearer YOUR_OBSERVABILITY_API_KEY" },
    traces: { content_mode: "metadata_only" },
    workspaces: { type: "all" }
  });

  console.log(destination.id);
  ```

  ```csharp C#
  using Anthropic.Models.Beta.Organization.TelemetryDestinations;

  AnthropicClient client = new();

  var destination = await client.Beta.Organization.TelemetryDestinations.Create(
      new()
      {
          EndpointUrl = "https://gateway.example.com/otlp",
          Headers = new Dictionary<string, string>
          {
              ["authorization"] = "Bearer YOUR_OBSERVABILITY_API_KEY",
          },
          Traces = new() { ContentMode = BetaTelemetryDestinationContentMode.MetadataOnly },
          Workspaces = new BetaTelemetryDestinationWorkspacesAllParams(),
      }
  );

  Console.WriteLine(destination.ID);
  ```

  ```go Go
  client := anthropic.NewClient()

  destination, err := client.Beta.Organization.TelemetryDestinations.New(
  	context.Background(),
  	anthropic.BetaOrganizationTelemetryDestinationNewParams{
  		EndpointURL: "https://gateway.example.com/otlp",
  		Headers: map[string]string{
  			"authorization": "Bearer YOUR_OBSERVABILITY_API_KEY",
  		},
  		Traces: anthropic.BetaTelemetryDestinationTracesParams{
  			ContentMode: anthropic.BetaTelemetryDestinationContentModeMetadataOnly,
  		},
  		Workspaces: anthropic.BetaTelemetryDestinationWorkspacesParamsUnion{
  			OfAll: &anthropic.BetaTelemetryDestinationWorkspacesAllParams{},
  		},
  	},
  )
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(destination.ID)
  ```

  ```java Java
  import com.anthropic.core.JsonValue;
  import com.anthropic.models.beta.organization.telemetrydestinations.BetaTelemetryDestinationContentMode;
  import com.anthropic.models.beta.organization.telemetrydestinations.BetaTelemetryDestinationTracesParams;
  import com.anthropic.models.beta.organization.telemetrydestinations.BetaTelemetryDestinationWorkspacesAllParams;
  import com.anthropic.models.beta.organization.telemetrydestinations.TelemetryDestinationCreateParams;

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      var params = TelemetryDestinationCreateParams.builder()
          .endpointUrl("https://gateway.example.com/otlp")
          .headers(TelemetryDestinationCreateParams.Headers.builder()
              .putAdditionalProperty("authorization", JsonValue.from("Bearer YOUR_OBSERVABILITY_API_KEY"))
              .build())
          .traces(BetaTelemetryDestinationTracesParams.builder()
              .contentMode(BetaTelemetryDestinationContentMode.METADATA_ONLY)
              .build())
          .workspaces(BetaTelemetryDestinationWorkspacesAllParams.builder().build())
          .build();
      var destination = client.beta().organization().telemetryDestinations().create(params);

      IO.println(destination.id());
  }
  ```

  ```php PHP
  $client = new Client();

  $destination = $client->beta->organization->telemetryDestinations->create(
      endpointURL: 'https://gateway.example.com/otlp',
      headers: ['authorization' => 'Bearer YOUR_OBSERVABILITY_API_KEY'],
      traces: ['contentMode' => 'metadata_only'],
      workspaces: ['type' => 'all'],
  );

  echo "{$destination->id}\n";
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  destination = client.beta.organization.telemetry_destinations.create(
    endpoint_url: "https://gateway.example.com/otlp",
    headers: {authorization: "Bearer YOUR_OBSERVABILITY_API_KEY"},
    traces: {content_mode: :metadata_only},
    workspaces: {type: :all}
  )

  puts destination.id
  ```
</CodeGroup>

생성 요청은 **Provider**를 제외한 Console의 설정을 받으므로, 관측성 플랫폼의 엔드포인트 URL과 헤더를 직접 입력하세요. 관측성 플랫폼이 허용하는 크기에 맞게 `traces.max_content_bytes` (csharp, go: `Traces.MaxContentBytes`; java, php: `traces.maxContentBytes`)를 바이트 단위로 설정하세요([콘텐츠 제한](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#content-limits) 참조). 대상을 나열, 변경 또는 삭제하거나 워크스페이스 하나를 추가하거나 제거하려면 텔레메트리 대상 API의 다른 메서드를 사용하세요.

## 2. 대상의 워크스페이스 선택

대상은 자신이 포함하는 워크스페이스의 스팬을 받습니다. 대상을 추가할 때 워크스페이스를 선택하세요. **Workspaces**에서 다음 중 하나를 선택하세요:

* **All:** [Default Workspace](https://platform.claude.com/docs/ko/manage-claude/workspaces#how-workspaces-work)와 나중에 생성되는 워크스페이스를 포함하여 조직의 모든 워크스페이스.
* **Selected:** 선택한 워크스페이스만.
* **All except:** **All**과 같지만 선택한 워크스페이스는 제외.

**Selected** 또는 **All except**의 경우, 옵션 아래에 나타나는 검색 상자에서 워크스페이스를 선택하세요. 나중에 대상의 워크스페이스를 변경하려면, **Workspace** 필터가 **All**로 표시된 상태에서 [OpenTelemetry 설정 페이지](https://platform.claude.com/settings/telemetry-destinations)의 해당 대상 **More actions** 메뉴를 여세요. **Edit**을 클릭한 다음 다른 **Workspaces** 옵션을 선택하거나 검색 상자에서 워크스페이스를 변경하세요.

조직에서 개발자 역할이 있고 어떤 워크스페이스에서 Workspace Admin 역할을 가지고 있다면, **Selected** 대상에 해당 워크스페이스를 추가하거나 대상에서 제거할 수 있습니다. **Workspace** 필터를 해당 워크스페이스로 설정한 다음 대상의 **More actions** 메뉴를 여세요. Claude Platform on AWS에서는 Console에 Workspace Admin 역할이 없습니다. 이 경우 [IAM 정책](https://platform.claude.com/docs/ko/api/claude-platform-on-aws-iam-actions)에 따라 누가 대상에 워크스페이스를 추가하거나 대상에서 제거할 수 있는지가 결정됩니다.

API는 [요청이나 세션이 실행되는 워크스페이스](https://platform.claude.com/docs/ko/manage-claude/workspaces#api-keys-and-resource-scoping)를 포함하는 모든 활성화된 대상으로 스팬을 전송합니다.

## 3. 스팬 도착 확인

1. Console에서 해당 대상이 적용되는 워크스페이스를 선택하고 [playground](https://platform.claude.com/playground)에서 Messages API 요청을 보내세요. 애플리케이션에서 Messages API 요청을 보내거나 해당 워크스페이스에서 Managed Agents 세션을 시작할 수도 있습니다. 세션을 시작하려면 [Claude Managed Agents 시작하기](https://platform.claude.com/docs/ko/managed-agents/quickstart)의 단계를 따르세요.
2. Console의 [OpenTelemetry 설정 페이지](https://platform.claude.com/settings/telemetry-destinations)에서 대상의 **Delivery** 열에 **Healthy**가 표시되는지 확인하세요. 약 1분 정도 걸릴 수 있습니다.
3. 관측성 플랫폼에서 `service.name` = `claude-platform`으로 필터링하여 요청의 스팬을 찾으세요.

열에 **Failing**이 표시되면 포인터를 그 위에 올려 마지막 오류를 확인하세요. 가능한 원인을 찾으려면 [전송 오류](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#delivery-errors)를 참조하세요.

## 4. API 스팬을 자체 트레이스에 연결

기본적으로 Messages API 요청이나 Managed Agents 세션의 API 스팬은 별도의 트레이스에 있습니다. 이를 애플리케이션의 트레이스에 연결하여 자체 스팬 아래에 중첩되도록 하세요.

API는 W3C [`traceparent` 헤더](https://www.w3.org/TR/trace-context/)에서 각 요청의 트레이스 컨텍스트를 읽습니다. 헤더의 sampled 플래그가 꺼져 있으면 API는 해당 요청의 스팬과 그 아래의 스팬을 생성하지 않습니다. Claude Platform on AWS는 아직 이 헤더를 지원하지 않습니다. sampled 플래그가 꺼져 있더라도 이 플랫폼으로 보내는 요청의 스팬은 항상 별도의 트레이스를 시작합니다.

<Tabs>
  <Tab title="Claude SDK 사용 시">
    API 스팬을 연결하려면 애플리케이션에서 [SDK 추적을 설정](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/sdk)하고 자체 스팬 중 하나 안에서 SDK를 호출하세요. 그러면 자체 스팬이 SDK 스팬을 포함하고, SDK 스팬이 API 스팬을 포함합니다. 플랫폼이 이를 하나의 트레이스로 표시할 수 있도록 이 모든 스팬을 같은 관측성 플랫폼으로 전송하세요. [Messages API 요청](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#a-messages-api-request)과 [Managed Agents 세션](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#a-managed-agents-session)의 예제 트레이스를 참조하세요.

    다음 예제는 자체 스팬 안에서 세션에 사용자 메시지를 보냅니다:

    <CodeGroup exclude="shell">
      ```python Python
      from opentelemetry import trace

      tracer = trace.get_tracer("my-service")

      with tracer.start_as_current_span("summarize_issues"):
          client.beta.sessions.events.send(
              session.id,
              events=[
                  {
                      "type": "user.message",
                      "content": [{"type": "text", "text": "Summarize the open issues"}],
                  },
              ],
          )
      ```

      ```typescript TypeScript
      import { trace } from "@opentelemetry/api";

      const tracer = trace.getTracer("my-service");

      await tracer.startActiveSpan("summarize_issues", async (span) => {
        try {
          await client.beta.sessions.events.send(session.id, {
            events: [
              {
                type: "user.message",
                content: [{ type: "text", text: "Summarize the open issues" }]
              }
            ]
          });
        } finally {
          span.end();
        }
      });
      ```

      ```csharp C#
      using System.Diagnostics;

      // 이 소스를 tracer provider에 com.anthropic.sdk.csharp와 함께 추가하세요.
      var source = new ActivitySource("MyService");

      using (source.StartActivity("summarize_issues"))
      {
          // parameters에는 user.message 이벤트가 담겨 있습니다.
          await client.Beta.Sessions.Events.Send(session.ID, parameters);
      }
      ```

      ```go Go
      // span이 포함된 컨텍스트를 전달하세요.
      ctx, span := otel.Tracer("my-service").Start(ctx, "summarize_issues")
      defer span.End()

      // params에는 user.message 이벤트가 담겨 있습니다.
      _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, params)
      ```

      ```java Java
      Tracer tracer = GlobalOpenTelemetry.getTracer("my-service");
      Span span = tracer.spanBuilder("summarize_issues").startSpan();

      try (Scope scope = span.makeCurrent()) {
          // params에는 user.message 이벤트가 담깁니다.
          client.beta().sessions().events().send(session.id(), params);
      } finally {
          span.end();
      }
      ```

      ```php PHP
      use OpenTelemetry\API\Globals;

      $tracer = Globals::tracerProvider()->getTracer('my-service');
      $span = $tracer->spanBuilder('summarize_issues')->startSpan();
      $scope = $span->activate();

      try {
          $client->beta->sessions->events->send(
              $session->id,
              events: [
                  [
                      'type' => 'user.message',
                      'content' => [
                          ['type' => 'text', 'text' => 'Summarize the open issues'],
                      ],
                  ],
              ],
          );
      } finally {
          $scope->detach();
          $span->end();
      }
      ```

      ```ruby Ruby
      tracer = OpenTelemetry.tracer_provider.tracer("my-service")

      tracer.in_span("summarize_issues") do
        client.beta.sessions.events.send_(
          session.id,
          events: [
            {
              type: "user.message",
              content: [{type: "text", text: "Summarize the open issues"}]
            }
          ]
        )
      end
      ```
    </CodeGroup>

    SDK는 애플리케이션의 프로파게이터(propagator)로 `traceparent` 헤더를 작성하며, 헤더의 sampled 플래그는 샘플러를 따릅니다. 애플리케이션이 B3와 같이 W3C 트레이스 컨텍스트 이외의 프로파게이터를 사용하는 경우, W3C 트레이스 컨텍스트 프로파게이터도 추가하세요.
  </Tab>

  <Tab title="다른 클라이언트 사용 시">
    예를 들어 Managed Agents 세션을 생성하거나 사용자 메시지를 보낼 때, 현재 스팬의 트레이스 컨텍스트를 요청 헤더에 복사하세요:

    <CodeGroup exclude="shell:CLI">
      ```bash cURL
      # TRACEPARENT는 사용자 서비스에서 활성 span의 traceparent입니다.
      # 예: 00-<trace-id>-<span-id>-01.
      curl -fsSL "https://api.anthropic.com/v1/sessions/$SESSION_ID/events" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01" \
        -H "content-type: application/json" \
        -H "traceparent: $TRACEPARENT" \
        -d '{
          "events": [
            {
              "type": "user.message",
              "content": [{"type": "text", "text": "Summarize the open issues"}]
            }
          ]
        }'
      ```

      ```python Python
      import os
      import httpx2
      from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator

      headers = {
          "x-api-key": os.environ["ANTHROPIC_API_KEY"],
          "anthropic-version": "2023-06-01",
          "anthropic-beta": "managed-agents-2026-04-01",
          "content-type": "application/json",
      }
      # 활성 span의 trace context를 요청 헤더에 복사합니다.
      TraceContextTextMapPropagator().inject(headers)

      httpx2.post(
          f"https://api.anthropic.com/v1/sessions/{os.environ['SESSION_ID']}/events",
          headers=headers,
          json={
              "events": [
                  {
                      "type": "user.message",
                      "content": [{"type": "text", "text": "Summarize the open issues"}],
                  },
              ],
          },
      )
      ```

      ```typescript TypeScript
      import { context, defaultTextMapSetter } from "@opentelemetry/api";
      import { W3CTraceContextPropagator } from "@opentelemetry/core";

      const headers: Record<string, string> = {
        "x-api-key": process.env.ANTHROPIC_API_KEY!,
        "anthropic-version": "2023-06-01",
        "anthropic-beta": "managed-agents-2026-04-01",
        "content-type": "application/json"
      };
      // 활성 span의 trace context를 요청 헤더에 복사합니다.
      new W3CTraceContextPropagator().inject(context.active(), headers, defaultTextMapSetter);

      await fetch(`https://api.anthropic.com/v1/sessions/${process.env.SESSION_ID}/events`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          events: [
            {
              type: "user.message",
              content: [{ type: "text", text: "Summarize the open issues" }]
            }
          ]
        })
      });
      ```

      ```csharp C#
      using System.Diagnostics;
      using System.Net.Http.Json;
      using OpenTelemetry;
      using OpenTelemetry.Context.Propagation;

      using var httpClient = new HttpClient();
      using var request = new HttpRequestMessage(
          HttpMethod.Post,
          $"https://api.anthropic.com/v1/sessions/{Environment.GetEnvironmentVariable("SESSION_ID")}/events")
      {
          // JsonContent는 content-type 헤더를 설정합니다.
          Content = JsonContent.Create(new
          {
              events = new[]
              {
                  new
                  {
                      type = "user.message",
                      content = new[] { new { type = "text", text = "Summarize the open issues" } },
                  },
              },
          }),
      };
      request.Headers.Add("x-api-key", Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY"));
      request.Headers.Add("anthropic-version", "2023-06-01");
      request.Headers.Add("anthropic-beta", "managed-agents-2026-04-01");

      // 활성 span의 trace context를 요청 헤더에 복사합니다.
      new TraceContextPropagator().Inject(
          new PropagationContext(Activity.Current?.Context ?? default, Baggage.Current),
          request,
          (carrier, key, value) => carrier.Headers.Add(key, value));

      await httpClient.SendAsync(request);
      ```

      ```go Go
      import (
      	"context"
      	"net/http"
      	"os"
      	"strings"

      	"go.opentelemetry.io/otel/propagation"
      )

      func sendMessage(ctx context.Context) error {
      	body := strings.NewReader(`{
      		"events": [
      			{
      				"type": "user.message",
      				"content": [{"type": "text", "text": "Summarize the open issues"}]
      			}
      		]
      	}`)
      	req, err := http.NewRequestWithContext(ctx, http.MethodPost,
      		"https://api.anthropic.com/v1/sessions/"+os.Getenv("SESSION_ID")+"/events", body)
      	if err != nil {
      		return err
      	}
      	req.Header.Set("x-api-key", os.Getenv("ANTHROPIC_API_KEY"))
      	req.Header.Set("anthropic-version", "2023-06-01")
      	req.Header.Set("anthropic-beta", "managed-agents-2026-04-01")
      	req.Header.Set("content-type", "application/json")

      	// 활성 span의 trace context를 요청 헤더에 복사합니다.
      	propagation.TraceContext{}.Inject(ctx, propagation.HeaderCarrier(req.Header))

      	resp, err := http.DefaultClient.Do(req)
      	if err != nil {
      		return err
      	}
      	return resp.Body.Close()
      }
      ```

      ```java Java
      import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
      import io.opentelemetry.context.Context;
      import java.net.URI;
      import java.net.http.HttpClient;
      import java.net.http.HttpRequest;
      import java.net.http.HttpResponse;

      void main() throws Exception {
          var request = HttpRequest.newBuilder(URI.create(
                  "https://api.anthropic.com/v1/sessions/" + System.getenv("SESSION_ID") + "/events"))
              .header("x-api-key", System.getenv("ANTHROPIC_API_KEY"))
              .header("anthropic-version", "2023-06-01")
              .header("anthropic-beta", "managed-agents-2026-04-01")
              .header("content-type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString("""
                  {
                    "events": [
                      {
                        "type": "user.message",
                        "content": [{"type": "text", "text": "Summarize the open issues"}]
                      }
                    ]
                  }
                  """));

          // 활성 span의 trace context를 요청 헤더에 복사합니다.
          W3CTraceContextPropagator.getInstance()
              .inject(Context.current(), request, HttpRequest.Builder::header);

          HttpClient.newHttpClient().send(request.build(), HttpResponse.BodyHandlers.ofString());
      }
      ```

      ```php PHP
      use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;

      $headers = [
          'x-api-key' => getenv('ANTHROPIC_API_KEY'),
          'anthropic-version' => '2023-06-01',
          'anthropic-beta' => 'managed-agents-2026-04-01',
          'content-type' => 'application/json',
      ];
      // 활성 span의 trace context(추적 컨텍스트)를 요청 헤더에 복사합니다.
      TraceContextPropagator::getInstance()->inject($headers);

      $ch = curl_init('https://api.anthropic.com/v1/sessions/' . getenv('SESSION_ID') . '/events');
      curl_setopt_array($ch, [
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_HTTPHEADER => array_map(
              static fn ($name, $value) => "$name: $value",
              array_keys($headers),
              $headers,
          ),
          CURLOPT_POSTFIELDS => json_encode([
              'events' => [
                  [
                      'type' => 'user.message',
                      'content' => [
                          ['type' => 'text', 'text' => 'Summarize the open issues'],
                      ],
                  ],
              ],
          ]),
      ]);
      curl_exec($ch);
      ```

      ```ruby Ruby
      require "json"
      require "net/http"
      require "opentelemetry"

      headers = {
        "x-api-key" => ENV.fetch("ANTHROPIC_API_KEY"),
        "anthropic-version" => "2023-06-01",
        "anthropic-beta" => "managed-agents-2026-04-01",
        "content-type" => "application/json"
      }
      # 활성 span의 trace context를 요청 헤더에 복사합니다.
      OpenTelemetry::Trace::Propagation::TraceContext.text_map_propagator.inject(headers)

      Net::HTTP.post(
        URI("https://api.anthropic.com/v1/sessions/#{ENV.fetch("SESSION_ID")}/events"),
        {
          events: [
            {
              type: "user.message",
              content: [{type: "text", text: "Summarize the open issues"}]
            }
          ]
        }.to_json,
        headers
      )
      ```
    </CodeGroup>
  </Tab>
</Tabs>

Messages API는 또한 [MCP connector](https://platform.claude.com/docs/ko/agents-and-tools/mcp-connector)로 MCP 서버를 연결하면 그 서버로 트레이스를 전달하므로, 해당 서버가 자체 스팬을 API 스팬 아래에 둘 수 있습니다. API가 MCP 서버로 보내는 각 요청과 알림에는 `params._meta`에 W3C `traceparent`가 포함됩니다. `tools/call` 요청에서는 해당 도구 사용의 [`anthropic.mcp_tool_use`](https://platform.claude.com/docs/ko/api/opentelemetry/spans#anthropic-tool-use-anthropic-mcp-tool-use-and-anthropic-custom-tool-use) 스팬을 가리킵니다. 그 외의 메시지에서는 [`anthropic.messages.create`](https://platform.claude.com/docs/ko/api/opentelemetry/spans#anthropic-messages-create) 스팬을 가리킵니다. `traceparent` 헤더의 sampled 플래그가 꺼져 있는 요청처럼 API가 트레이스하지 않는 요청의 경우, API는 `traceparent`를 보내지 않습니다.

## 다음 단계

<CardGroup cols={2}>
  <Card title="SDK 추적 설정" icon="code-brackets" href="https://platform.claude.com/docs/ko/manage-claude/opentelemetry/sdk">
    클라이언트에 대한 관측성을 확보합니다.
  </Card>

  <Card title="예제 트레이스 보기" icon="sitemap" href="https://platform.claude.com/docs/ko/manage-claude/opentelemetry#example-traces-and-how-to-read-them">
    클라이언트에서 Claude API로 이어지는 요청을 따라가 보세요.
  </Card>
</CardGroup>

## 전송 및 상태

API가 대상으로 보내는 스팬에는 다음 규칙이 적용됩니다.

* **최소 1회 전송:** 대상은 같은 스팬을 두 번 이상 받을 수 있습니다. 중복을 제거하려면 트레이스 ID와 스팬 ID를 사용하세요.
* **최선 노력(best effort):** 스팬은 전송 전에 유실될 수 있습니다. API는 유실된 스팬을 다시 보낼 수 없습니다.
* **압축:** API는 모든 전송의 첫 번째 요청을 gzip으로 압축하고 `Content-Encoding: gzip` 헤더를 설정합니다. 엔드포인트가 해당 요청에 `400` 또는 `415` 응답을 반환하면, API는 즉시 압축 없이 같은 스팬을 다시 보냅니다.
* **재시도:** 타임아웃, 연결·TLS·DNS 실패, 또는 `408`, `429`, `5xx` 응답 후에는 API가 제한된 횟수만큼 나중에 전송을 다시 시도합니다. 다른 실패에 대해서는 재시도하지 않습니다.
* **타임아웃:** 각 요청에 대해 엔드포인트는 5초 이내에 TLS 핸드셰이크를 완료하고 5초 이내에 응답 헤더를 보내야 합니다. 각 전송은 압축 없이 다시 보내는 요청을 포함하여 총 10초 후에 타임아웃됩니다.
* **사용자 에이전트:** 각 요청에는 `User-Agent: Claude-Platform` 헤더가 있습니다.

**Delivery** 열은 각 대상의 상태를 보여줍니다. 상태는 최대 1분까지 늦게 반영될 수 있습니다.

| 전송 상태        | 의미                                                  |
| ------------ | --------------------------------------------------- |
| **Healthy**  | 마지막 전송이 성공했습니다.                                     |
| **Failing**  | 마지막 전송이 실패했습니다. 오류를 보려면 **Failing** 위에 포인터를 올리세요.   |
| **No data**  | API가 아직 전송을 시도하지 않았습니다.                             |
| **Disabled** | 대상이 스팬을 받지 않습니다. 이유를 보려면 **Disabled** 위에 포인터를 올리세요. |

API는 다음과 같은 경우 대상을 비활성화합니다:

* 엔드포인트가 `3xx` 리디렉션을 보내는 경우.
* 엔드포인트의 호스트 이름이 공용이 아닌 IP 주소로 확인되는 경우.
* 24시간 동안 모든 전송이 실패하는 경우.

API는 비활성화된 대상에 span을 보내지 않습니다. 내보내기를 다시 시작하려면 원인을 해결하세요. 그런 다음 [OpenTelemetry 설정 페이지](https://platform.claude.com/settings/telemetry-destinations)의 **Workspace** 필터가 **All**을 표시하는 상태에서, 대상의 **More actions** 메뉴에서 **Enable**을 클릭하세요. 약 1분 이내에 내보내기가 다시 시작됩니다.

대상을 변경하려면 **Workspace** 필터가 **All**을 표시하는 상태에서 해당 대상의 **More actions** 메뉴를 여세요. 그런 다음 **Edit**, **Disable**, **Enable** 또는 **Delete**를 클릭하세요. **OTLP endpoint URL**의 호스트를 변경하는 경우 헤더 값을 다시 입력하세요. 대상에 **OTLP traces endpoint URL**이 있는 경우, 동일한 새 호스트를 지정하거나 값을 지우세요.

## 제한

| 제한                  | 값         |
| ------------------- | --------- |
| 조직당 대상(비활성화된 대상 포함) | 10        |
| 대상에 대해 선택하는 워크스페이스  | 1,000     |
| 대상당 요청 헤더           | 20        |
| 헤더 이름 길이            | 128바이트    |
| 헤더 값 길이             | 4,096바이트  |
| 모든 헤더 이름과 값의 합계 길이  | 16,384바이트 |
| 엔드포인트 URL 길이        | 2,048자    |

## 문제 해결

| 문제                                              | 확인할 사항                                                                                                                                                                                                                         |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| API span이 없음                                    | 워크스페이스를 포함하는 활성화된 대상이 필요합니다. [대상의 워크스페이스 선택](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#choose-workspaces-for-a-destination)을 참조하세요. 대상이 워크스페이스를 포함하기 시작한 후 해당 워크스페이스의 span이 도착하기까지 최대 1분이 걸릴 수 있습니다. |
| **Delivery** 열에 **Failing**이 표시됨                | 포인터를 그 위에 올려 마지막 오류를 확인하세요. 가능한 원인을 찾으려면 [전송 오류](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#delivery-errors)를 참조하세요.                                                                                  |
| **Delivery** 열에 **Disabled**가 표시됨               | 포인터를 그 위에 올려 이유를 확인하세요. 이유에 대해서는 [API가 대상을 비활성화하는 경우](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#delivery-and-health)를 참조하세요.                                                                         |
| 대상이 **Full content**로 설정되어 있지만 스팬에 프롬프트나 응답이 없음 | 조직에 Anthropic과의 HIPAA, FERPA 또는 K-12 데이터 처리 제한이 있을 수 있습니다. [콘텐츠 모드](https://platform.claude.com/docs/ko/manage-claude/opentelemetry#choose-a-content-mode)를 참조하세요.                                                             |

### 전송 오류

전송이 실패하면 **Delivery** 열에 **Failing**이 표시됩니다. 그 위에 포인터를 올려 오류를 확인한 다음, 가능한 원인을 찾아보세요:

| 오류                                                 | 가능한 원인                                                                                                                            |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| HTTP error (status 401) 또는 HTTP error (status 403) | 잘못된 인증 헤더                                                                                                                         |
| HTTP error (status 404)                            | **OTLP endpoint URL** 또는 **OTLP traces endpoint URL**의 잘못된 경로                                                                     |
| DNS resolution failure                             | 엔드포인트의 호스트 이름이 확인되지 않음                                                                                                            |
| TLS failure                                        | 엔드포인트의 인증서 등과 관련된 TLS 문제                                                                                                          |
| Timeout                                            | 엔드포인트가 제시간에 응답하지 않았습니다. [전송 타임아웃](https://platform.claude.com/docs/ko/manage-claude/opentelemetry/api#delivery-and-health)을 참조하세요 |
| Connection failure                                 | API가 엔드포인트에 연결할 수 없음                                                                                                              |
