> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

Claude AI metrics

Loading version...

Port's Claude integration allows you to ingest Claude AI (Enterprise) and Claude Platform usage, cost, and Claude Code analytics metrics into your software catalog.

Deployment modes

The integration runs in one of two deployment modes, controlled by the isClaudeEnterprise configuration option. Each mode targets a different set of Claude API endpoints and requires an API key with a different scope.

ModeisClaudeEnterpriseResources syncedRequired API key scope
Claude AI (Enterprise)true (default)claude-ai-user-activity, claude-ai-user-usage, claude-ai-user-costread:analytics
Claude Platformfalseclaude-platform-usage-record, claude-platform-cost-record, claude-platform-code-analyticsapi:admin

Use Claude AI (Enterprise) mode to track per-user engagement, token usage, and cost for everyday Claude usage such as the web app and Claude Code. Use Claude Platform mode to track aggregated usage and cost for Claude Platform API consumption, typically used by developers.

Blueprints for both modes

When initializePortResources is enabled, the integration creates the default blueprints for both Claude AI and Claude Platform, regardless of the selected mode. Only the resources for the active mode are populated with data.

Prerequisites

  • An Anthropic API key for the deployment mode you want to use:
    • Claude AI (Enterprise) mode requires an analytics API key with the read:analytics scope, minted by a Primary Owner from claude.ai/analytics/api-keys.
    • Claude Platform mode requires an Admin API key with the api:admin scope and access to the usage and cost reports.
  • Your Port user role is set to Admin.

Setup

Choose one of the installation methods below. If you are not sure which option to use, review the installation methods overview.

Configuration

Port integrations use a YAML mapping block to ingest data from the third-party API into Port.

The mapping uses the JQ JSON processor to transform fields and shape incoming Claude data into Port entities.

Default mapping configuration

This is the default mapping configuration for this integration. It maps the Claude AI (Enterprise) resources, which are synced in the default deployment mode:

Default mapping configuration (Click to expand)
resources:
- kind: claude-ai-user-activity
selector:
query: 'true'
timeFrame: 30
port:
entity:
mappings:
identifier: .user.id + "-" + .__date
title: (.user.email_address // .user.id) + " - " + .__date
blueprint: '"claude_ai_user_activity"'
properties:
record_date: .__date + "T00:00:00Z"
user_id: .user.id
email: .user.email_address
chat_conversation_count: (.chat_metrics.distinct_conversation_count // 0)
chat_message_count: (.chat_metrics.message_count // 0)
chat_thinking_message_count: (.chat_metrics.thinking_message_count // 0)
chat_skills_used_count: (.chat_metrics.distinct_skills_used_count // 0)
chat_connectors_used_count: (.chat_metrics.connectors_used_count // 0)
code_session_count: (.claude_code_metrics.core_metrics.distinct_session_count // 0)
code_commit_count: (.claude_code_metrics.core_metrics.commit_count // 0)
code_pull_request_count: (.claude_code_metrics.core_metrics.pull_request_count // 0)
lines_added: (.claude_code_metrics.core_metrics.lines_of_code.added_count // 0)
lines_removed: (.claude_code_metrics.core_metrics.lines_of_code.removed_count // 0)
edit_tool_accepted: (.claude_code_metrics.tool_actions.edit_tool.accepted_count // 0)
edit_tool_rejected: (.claude_code_metrics.tool_actions.edit_tool.rejected_count // 0)
write_tool_accepted: (.claude_code_metrics.tool_actions.write_tool.accepted_count // 0)
write_tool_rejected: (.claude_code_metrics.tool_actions.write_tool.rejected_count // 0)
multi_edit_tool_accepted: (.claude_code_metrics.tool_actions.multi_edit_tool.accepted_count // 0)
multi_edit_tool_rejected: (.claude_code_metrics.tool_actions.multi_edit_tool.rejected_count // 0)
notebook_edit_tool_accepted: (.claude_code_metrics.tool_actions.notebook_edit_tool.accepted_count // 0)
notebook_edit_tool_rejected: (.claude_code_metrics.tool_actions.notebook_edit_tool.rejected_count // 0)
web_search_count: (.web_search_count // 0)
chat_projects_used_count: (.chat_metrics.distinct_projects_used_count // 0)
chat_projects_created_count: (.chat_metrics.distinct_projects_created_count // 0)
chat_artifacts_created_count: (.chat_metrics.distinct_artifacts_created_count // 0)
chat_files_uploaded_count: (.chat_metrics.distinct_files_uploaded_count // 0)
chat_shared_conversations_viewed_count: (.chat_metrics.shared_conversations_viewed_count // 0)
office_session_count: >-
([.office_metrics.excel.distinct_session_count,
.office_metrics.powerpoint.distinct_session_count,
.office_metrics.word.distinct_session_count,
.office_metrics.outlook.distinct_session_count] | map(. // 0) | add)
office_message_count: >-
([.office_metrics.excel.message_count,
.office_metrics.powerpoint.message_count,
.office_metrics.word.message_count,
.office_metrics.outlook.message_count] | map(. // 0) | add)
cowork_session_count: (.cowork_metrics.distinct_session_count // 0)
cowork_message_count: (.cowork_metrics.message_count // 0)
design_session_count: (.design_metrics.distinct_session_count // 0)
design_message_count: (.design_metrics.message_count // 0)
- kind: claude-ai-user-usage
selector:
query: 'true'
startingAt: '2026-01-01T00:00:00Z'
port:
entity:
mappings:
identifier: >-
.actor.user_id + "-" + .__starting_at[:10] + "-" + (.product // "all")
+ "-" + (.model // "all")
title: (.actor.name // .actor.email // .actor.user_id) + " - " + .__starting_at[:10]
blueprint: '"claude_ai_user_usage"'
properties:
record_date: .__starting_at
range_end: .__ending_at
user_id: .actor.user_id
user_name: .actor.name
email: .actor.email
deleted: (.actor.deleted // false)
product: .product
model: .model
context_window: .context_window
inference_geo: .inference_geo
speed: .speed
uncached_input_tokens: (.uncached_input_tokens // 0)
cache_creation_1h_tokens: (.cache_creation.ephemeral_1h_input_tokens // 0)
cache_creation_5m_tokens: (.cache_creation.ephemeral_5m_input_tokens // 0)
cache_read_input_tokens: (.cache_read_input_tokens // 0)
output_tokens: (.output_tokens // 0)
total_tokens: (.total_tokens // 0)
web_search_requests: (.server_tool_use.web_search_requests // 0)
requests: (.requests // 0)
- kind: claude-ai-user-cost
selector:
query: 'true'
startingAt: '2026-01-01T00:00:00Z'
port:
entity:
mappings:
identifier: >-
.actor.user_id + "-" + .__starting_at[:10] + "-" + (.product // "all")
+ "-" + (.model // "all")
title: (.actor.name // .actor.email // .actor.user_id) + " - " + .__starting_at[:10]
blueprint: '"claude_ai_user_cost"'
properties:
record_date: .__starting_at
range_end: .__ending_at
user_id: .actor.user_id
user_name: .actor.name
email: .actor.email
deleted: (.actor.deleted // false)
product: .product
model: .model
currency: (.currency // "USD")
amount: ((.amount // "0") | tonumber) / 100
list_amount: ((.list_amount // "0") | tonumber) / 100
requests: (.requests // 0)

Advanced selector configuration

The available selectors depend on the active deployment mode. query applies to all resource kinds in both modes and defaults to 'true'.

claude-ai-user-activity

This resource returns data for one specific day per call. The endpoint only returns data that is at least three days old, so the date window always ends roughly three days ago. To control the start of the window, provide one of timeFrame or startingDate (mutually exclusive). When you set neither, the integration defaults to a 30-day lookback.

FieldFormatDescription
timeFramenumberNumber of days to look back, ending around three days ago. Must be greater than 0. Mutually exclusive with startingDate. Defaults to 30 when neither field is set.
startingDateYYYY-MM-DDIterates from this date once per day up to around three days ago, clamped to 2026-01-01 (the earliest available data). Use for historical backfills. Mutually exclusive with timeFrame.

Using timeFrame (recommended for ongoing syncs):

selector:
query: 'true'
timeFrame: 30 # look back 30 days, one API call per day

Using startingDate (for a historical backfill):

selector:
query: 'true'
startingDate: '2026-01-01' # iterate from this date, one API call per day

claude-ai-user-usage and claude-ai-user-cost

These resources return per-user data across a date range and share the same selector fields.

FieldTypeDescription
startingAtstringRFC 3339 UTC start of the range (inclusive), e.g. 2026-01-01T00:00:00Z. Automatically clamped to the last 31 days and no earlier than 2026-01-01. Defaults to 2026-01-01T00:00:00Z.
endingAtstringRFC 3339 UTC end of the range (exclusive). Defaults to now. The range spans at most 31 days.
excludeDeletedUsersbooleanWhen true, rows for deleted users are omitted. Defaults to false.
productsarrayFilter to specific seat-based product surfaces. Allowed values: chat, claude_code, cowork, office_agent, claude_in_chrome, claude_design.
modelsarrayFilter to specific model names.
groupByarrayBreak each user's row out by the given dimensions. Allowed values: product, model, context_window, inference_geo, speed.
contextWindowsarrayFilter to specific context-window pricing tiers. Allowed values: 0-200k, 200k-1M.
inferenceGeosarrayFilter to specific inference regions. Allowed values: global, us, not_available.
speedsarrayFilter to fast or standard inference mode. Allowed values: fast, standard.
selector:
query: 'true'
startingAt: '2026-01-01T00:00:00Z'
endingAt: '2026-01-31T00:00:00Z'
excludeDeletedUsers: false
products: [chat, claude_code]
groupBy: [product, model]

Mapping & examples per resource

To view and test the integration mapping against sample API responses, use the jq playground in your data sources page.

Monitoring and sync status

To learn more about how to monitor and check the sync status of your integration, see the relevant documentation.

Migration guide

If you currently ingest Claude metrics through a custom Ocean integration, migrate to the dedicated Claude AI integration:

  1. Create the new Claude AI data source from your data sources page.
  2. Copy your existing Claude API credentials into the new integration setup.
  3. Move your custom blueprint and mapping configuration into the dedicated integration.
  4. Run a sync and validate that records are ingested for all enabled resources.
  5. Disable the legacy custom Ocean Claude integration after validation.

Visualize Claude metrics