← Agent Frameworks 🕐 10 min read
Agent Frameworks

AWS CUR 2.0 for Bedrock Token FinOps — Complete Field Guide

CUR 2.0 (AWS Data Exports) is the correct instrument for invoice-accurate Bedrock spend by model and IAM identity.

See also (wiki): llm-cost-attribution · agentic-token-governance · ai-budget-cfo-decisions

Pillar: 19 — Agent Frameworks & Observability
Scope: Fortune 500 enterprise running LiteLLM as AI gateway routing to Amazon Bedrock
Research date: 2026-06-16
Contract strength: Official docs verified (primary); community-pattern (secondary)


CUR 2.0 (AWS Data Exports) is the correct instrument for invoice-accurate Bedrock spend by model and IAM identity. It does not produce per-request rows — the finest grain is per-usage-type per day. For per-prompt attribution (team, project, user) behind a gateway like LiteLLM, the correct architecture is:

  1. IAM principal attribution (CUR 2.0 line_item_iam_principal + iamPrincipal/ tags) for dollar-level attribution by role/team → flows directly into billing
  2. Request metadata (X-Amzn-Bedrock-Request-Metadata header or requestMetadata body field) for per-prompt token counts → flows only into invocation logs, not CUR
  3. Invocation logs → Athena for per-call detail; join to CUR at model+usage-type grain for invoice reconciliation

IAM principal attribution became available April 8, 2026. It requires CUR 2.0 with INCLUDE_IAM_PRINCIPAL_DATA = TRUE enabled in the Data Exports configuration.


1. CUR 2.0 Column Structure for Bedrock

Core line-item columns (official docs verified)

Column Type Bedrock-specific value / pattern
line_item_product_code string AmazonBedrock
line_item_usage_type string {REGION}-{MODEL}-{token-type}[-{tier}][-cross-region-global] — see table below
line_item_operation string InvokeModelInference or InvokeModelStreamingInference (as of Jan 2026; was generic Usage before)
line_item_usage_amount decimal Token count for the aggregation period (not request count)
line_item_unblended_cost decimal Dollar cost for the line item
line_item_net_unblended_cost decimal Cost after credits/discounts
line_item_line_item_type string Usage (on-demand); Tax; Credit; SavingsPlanCoveredUsage (if covered by Bedrock Savings Plan)
line_item_usage_start_date timestamp Start of the aggregation window (hourly or daily per export config)
line_item_usage_end_date timestamp End of the aggregation window
line_item_resource_id string Present only when INCLUDE_RESOURCES = TRUE; contains inference profile ARN if request used one
line_item_iam_principal string CUR 2.0 only, requires INCLUDE_IAM_PRINCIPAL_DATA = TRUE. IAM ARN of the Bedrock caller. See formats below.
product_servicecode string AmazonBedrock
product_region string AWS region code (e.g., us-east-1)
product_from_location string Human-readable region name (e.g., US East (N. Virginia))
tags map<string,string> Nested map of all cost-allocation tag key-values with prefixes

line_item_usage_type format patterns

{region}-{model}-{token-type}                         # standard tier, in-region
{region}-{model}-{token-type}-{tier}                  # priority or flex tier
{region}-{model}-mantle-{token-type}-standard         # bedrock-mantle endpoint
{region}-{model}-{token-type}-cross-region-global     # cross-region inference

Region prefix uses AWS billing region codes: USE1 = us-east-1, USW2 = us-west-2, EUW1 = eu-west-1, etc.

Concrete line_item_usage_type examples (official docs verified, 2026)

Usage type Model Tier Token type Routing
USE1-Claude4.6Sonnet-input-tokens Claude Sonnet 4.6 Standard Input In-region
USE1-Claude4.6Sonnet-output-tokens Claude Sonnet 4.6 Standard Output In-region
USE1-Claude4.6Sonnet-cache-read-input-token-count Claude Sonnet 4.6 Standard Cache read In-region
USE1-Claude4.6Sonnet-cache-write-input-token-count Claude Sonnet 4.6 Standard Cache write In-region
USE1-Claude4.6Sonnet-output-tokens-cross-region-global Claude Sonnet 4.6 Standard Output Cross-region
USE1-Claude4.6Opus-input-tokens Claude Opus 4.6 Standard Input In-region
USE1-Nova2.0Lite-input-tokens-flex Amazon Nova 2 Lite Flex Input In-region
USE1-gpt-oss-120b-output-tokens-priority OpenAI gpt-oss-120b Priority Output In-region
USE1-openai.gpt-oss-120b-mantle-input-tokens-standard OpenAI gpt-oss-120b Standard Input In-region (mantle)

Four token types — all must be summed for accurate reconciliation

Token type line_item_usage_type pattern Relative price vs input
Input tokens *-input-tokens or *-mantle-input-tokens-* 1x (baseline)
Output tokens *-output-tokens or *-mantle-output-tokens-* ~3–5x input price
Cache read tokens *-cache-read-input-token-count ~0.1x input price (significantly cheaper)
Cache write tokens *-cache-write-input-token-count ~1.25x input price (more expensive than input)

Critical: If you only SUM input + output tokens, your totals will not match your bill for workloads using prompt caching. Cache read and write are separate line items with different unit prices.

line_item_iam_principal column formats

Available only in CUR 2.0 with INCLUDE_IAM_PRINCIPAL_DATA = TRUE (data starts April 8, 2026):

Identity type ARN format
IAM user arn:aws:iam::123456789012:user/alice
IAM role (assumed) arn:aws:sts::123456789012:assumed-role/AppRole/session-name
Federated user arn:aws:sts::123456789012:assumed-role/Role/user@acme.org
Bedrock API Key arn:aws:iam::123456789012:user/BedrockAPIKey-234s

For LiteLLM gateway: Bedrock records the gateway’s IAM role as the caller. To get per-team or per-project attribution, the gateway must call STS AssumeRole per tenant with a per-tenant RoleSessionName or session tags. The resulting ARN (assumed-role/gateway-role/tenant-name) flows into line_item_iam_principal.

Tags column — iamPrincipal/ prefix

IAM principal tags appear in the tags map column with the prefix iamPrincipal/:

{
  "iamPrincipal/department": "data-engineering",
  "iamPrincipal/cost-center": "CC-1042",
  "iamPrincipal/team": "finops-product",
  "iamPrincipal/project": "customer-360",
  "resourceTags/Environment": "prod",
  "accountTag/CostCenter": "CC-1042"
}

Full tag prefix taxonomy in CUR 2.0 tags column:

Prefix Source
resourceTags/ Tags on AWS resources (e.g., application inference profile ARN)
iamPrincipal/ Tags on IAM users or roles making Bedrock calls
userAttribute/ User attributes imported from IAM Identity Center
accountTag/ Tags at the AWS account level
costCategory/ Tags derived from AWS Cost Categories

2. CUR 2.0 vs CUR 1.0 for Bedrock

Capability Legacy CUR 1.0 CUR 2.0 (Data Exports)
Per-token type line items Yes Yes
line_item_usage_type with model+token-type Yes Yes
line_item_operation with InvokeModelInference Yes (since Jan 2026) Yes (since Jan 2026)
line_item_iam_principal column No Yes (requires INCLUDE_IAM_PRINCIPAL_DATA = TRUE, data from Apr 8, 2026)
iamPrincipal/ tag prefix in tags No Yes
Fixed schema (no monthly schema drift) No Yes — 125 fixed columns
Nested tags map column No (flat columns) Yes (also queryable as flat via dot operator)
bill_payer_account_name No Yes
line_item_usage_account_name No Yes
SQL table name in Athena Varies by export name COST_AND_USAGE_REPORT
Configurable time granularity Hourly or daily HOURLY, DAILY, or MONTHLY

Key migration note: product['product_name'] in CUR 2.0 replaces product_product_name in legacy CUR. All product_* flat columns become product['key'] map-style references in CUR 2.0 unless you use the dot operator: product.servicecode.

IAM principal attribution is CUR 2.0 only. There is no equivalent in legacy CUR.


3. Athena Queries

Prerequisites

-- CUR 2.0 table is named COST_AND_USAGE_REPORT by default in Data Exports
-- Replace 'your_cur_database' with the Glue database name from your Data Export config
-- Filter on line_item_usage_start_date — table is partitioned by date; this dramatically reduces scan cost

Query 1: Bedrock spend by model and token type (daily)

SELECT
    DATE_TRUNC('day', line_item_usage_start_date) AS usage_date,
    line_item_usage_type,
    -- Extract model name from usage type (everything between first and second '-' after region)
    REGEXP_EXTRACT(line_item_usage_type, '^[^-]+-(.+?)-(input|output|cache)', 1) AS model,
    CASE
        WHEN line_item_usage_type LIKE '%-input-tokens%'              THEN 'input'
        WHEN line_item_usage_type LIKE '%-output-tokens%'             THEN 'output'
        WHEN line_item_usage_type LIKE '%-cache-read-input-token%'    THEN 'cache_read'
        WHEN line_item_usage_type LIKE '%-cache-write-input-token%'   THEN 'cache_write'
        ELSE 'other'
    END AS token_type,
    SUM(line_item_usage_amount)        AS total_tokens,
    SUM(line_item_unblended_cost)      AS total_cost_usd
FROM your_cur_database.COST_AND_USAGE_REPORT
WHERE line_item_product_code = 'AmazonBedrock'
  AND line_item_line_item_type = 'Usage'
  AND line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
GROUP BY 1, 2, 3, 4
ORDER BY usage_date DESC, total_cost_usd DESC;

Query 2: Bedrock spend by IAM principal (requires CUR 2.0 with IAM principal enabled)

SELECT
    DATE_TRUNC('day', line_item_usage_start_date) AS usage_date,
    line_item_iam_principal,
    -- Extract session name from assumed-role ARN for human-readable identity
    REGEXP_EXTRACT(line_item_iam_principal, 'assumed-role/[^/]+/(.+)$', 1) AS session_or_user,
    line_item_usage_type,
    SUM(line_item_usage_amount)    AS total_tokens,
    SUM(line_item_unblended_cost)  AS total_cost_usd
FROM your_cur_database.COST_AND_USAGE_REPORT
WHERE line_item_product_code = 'AmazonBedrock'
  AND line_item_line_item_type = 'Usage'
  AND line_item_iam_principal IS NOT NULL
  AND line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
GROUP BY 1, 2, 3, 4
ORDER BY usage_date DESC, total_cost_usd DESC;

Query 3: Bedrock spend by team tag (iamPrincipal/team)

SELECT
    DATE_TRUNC('day', line_item_usage_start_date) AS usage_date,
    tags['iamPrincipal/team']                      AS team,
    tags['iamPrincipal/project']                   AS project,
    CASE
        WHEN line_item_usage_type LIKE '%-Claude%'   THEN 'Anthropic Claude'
        WHEN line_item_usage_type LIKE '%-Nova%'     THEN 'Amazon Nova'
        WHEN line_item_usage_type LIKE '%-Titan%'    THEN 'Amazon Titan'
        WHEN line_item_usage_type LIKE '%-llama%'    THEN 'Meta Llama'
        ELSE 'Other'
    END AS model_family,
    SUM(line_item_usage_amount)    AS total_tokens,
    SUM(line_item_unblended_cost)  AS total_cost_usd
FROM your_cur_database.COST_AND_USAGE_REPORT
WHERE line_item_product_code = 'AmazonBedrock'
  AND line_item_line_item_type = 'Usage'
  AND tags['iamPrincipal/team'] IS NOT NULL
  AND line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
GROUP BY 1, 2, 3, 4
ORDER BY usage_date DESC, total_cost_usd DESC;

Query 4: Invocation logs — per-prompt token detail with request metadata (S3 → Athena)

-- Prerequisite: CREATE EXTERNAL TABLE for Bedrock invocation logs in S3
-- S3 path pattern: s3://bucket/prefix/AWSLogs/{accountId}/BedrockModelInvocationLogs/{region}/

SELECT
    DATE_TRUNC('hour', from_iso8601_timestamp(timestamp)) AS hour,
    requestmetadata['team']         AS team,
    requestmetadata['project']      AS project,
    requestmetadata['environment']  AS env,
    modelid,
    identity.arn                    AS caller_arn,
    COUNT(*)                        AS request_count,
    SUM(input.inputtokencount)      AS input_tokens,
    SUM(output.outputtokencount)    AS output_tokens,
    -- Estimate cost; replace rates with current Bedrock pricing
    SUM(input.inputtokencount)  * 0.000003  AS est_input_cost_usd,   -- Claude Haiku example rate
    SUM(output.outputtokencount) * 0.000015 AS est_output_cost_usd
FROM bedrocklogsdb.bedrocklogs
WHERE dt >= '2026/06/01'            -- partition filter: YYYY/MM/DD
GROUP BY 1, 2, 3, 4, 5, 6
ORDER BY hour DESC, input_tokens DESC;

Query 5: CUR + invocation logs join (model+usage-type grain reconciliation)

-- Join at model+day grain — CUR has no per-requestId identifier
-- This confirms that your estimated costs from logs track to the invoice

WITH cur_daily AS (
    SELECT
        DATE_TRUNC('day', line_item_usage_start_date) AS day,
        line_item_usage_type,
        SUM(line_item_usage_amount)   AS cur_tokens,
        SUM(line_item_unblended_cost) AS cur_cost_usd
    FROM your_cur_database.COST_AND_USAGE_REPORT
    WHERE line_item_product_code = 'AmazonBedrock'
      AND line_item_line_item_type = 'Usage'
    GROUP BY 1, 2
),
logs_daily AS (
    SELECT
        DATE_TRUNC('day', from_iso8601_timestamp(timestamp)) AS day,
        modelid,
        SUM(input.inputtokencount)  AS log_input_tokens,
        SUM(output.outputtokencount) AS log_output_tokens
    FROM bedrocklogsdb.bedrocklogs
    GROUP BY 1, 2
)
SELECT
    c.day,
    c.line_item_usage_type,
    c.cur_tokens,
    l.log_input_tokens + l.log_output_tokens AS log_tokens,
    c.cur_cost_usd,
    (c.cur_tokens - (l.log_input_tokens + l.log_output_tokens)) AS token_gap
FROM cur_daily c
LEFT JOIN logs_daily l
    ON c.day = l.day
    AND c.line_item_usage_type LIKE '%' || SPLIT_PART(l.modelid, '.', 2) || '%'
ORDER BY c.day DESC, ABS(token_gap) DESC;

Query 6: Cost per million tokens by model (CUDOS-style)

SELECT
    REGEXP_EXTRACT(line_item_usage_type, '^[^-]+-(.+?)-(input|output|cache)', 1) AS model,
    CASE
        WHEN line_item_usage_type LIKE '%-input-tokens%'              THEN 'input'
        WHEN line_item_usage_type LIKE '%-output-tokens%'             THEN 'output'
        WHEN line_item_usage_type LIKE '%-cache-read-input-token%'    THEN 'cache_read'
        WHEN line_item_usage_type LIKE '%-cache-write-input-token%'   THEN 'cache_write'
    END AS token_type,
    SUM(line_item_usage_amount)                                                          AS total_tokens,
    SUM(line_item_unblended_cost)                                                        AS total_cost_usd,
    SUM(line_item_unblended_cost) / NULLIF(SUM(line_item_usage_amount), 0) * 1000000    AS cost_per_million_tokens
FROM your_cur_database.COST_AND_USAGE_REPORT
WHERE line_item_product_code = 'AmazonBedrock'
  AND line_item_line_item_type = 'Usage'
  AND line_item_usage_start_date >= DATE_ADD('day', -30, CURRENT_DATE)
GROUP BY 1, 2
ORDER BY total_cost_usd DESC;

4. FinOps Dashboard Patterns

CUDOS — Cloud Intelligence Dashboards (official AWS open-source, QuickSight)

Repo: aws-solutions-library-samples/cloud-intelligence-dashboards-framework
Deployment: CLI tool or CloudFormation; deploys in under 30 minutes
Bedrock coverage added by version:

CUDOS version Bedrock capability added
5.0.0 Bedrock section: daily cost per resource (model), spend by model family
5.5 Guardrails added to Bedrock UsageType Group calculated field
5.6 Spend per UsageType Group: Image Generation, Input Tokens, Output Tokens, Model Units, Model Customization
5.8.0 “Cost per 1000 Tokens by Usage Type Group” visual; Group By control (Account, Region, Pricing Model, Usage Type Group, Model, IAM Principal Tags)
5.8.1 Switched to cost per 1M tokens; “Amazon Bedrock Cost per Million Tokens by Model (Top 5 Models)”; “Bedrock Spend per Resource” (last 7 months)

Coverage gap: CUDOS does not yet have a pre-built view filtered to a single gateway role’s IAM principal spend. That requires a custom QuickSight calculated field or filtered analysis on iamPrincipal/team.

AWS-native: QuickSight + CUR + Bedrock invocation logs

Official blog: Monitor and optimize your Amazon Bedrock usage with Amazon Athena and Amazon QuickSight

Architecture:

Bedrock invocation logs → S3 → Glue Catalog → Athena → QuickSight
CUR 2.0                 → S3 → Glue Catalog → Athena → QuickSight

Typical dashboard panels:

  • Average token usage and total cost (pie chart by model)
  • Average task token usage over time (line chart)
  • Latency vs token count scatter
  • Cost per team/project (if request metadata or IAM tags present)

Grafana (community pattern, not officially supported)

Pattern: Athena data source plugin → Grafana dashboard. No official AWS-maintained Bedrock dashboard for Grafana exists as of mid-2026. Teams build custom dashboards using Athena data source with the queries above.

aws-usage-queries (deprecated)

aws-samples/aws-usage-queries — bootstraps Athena for CUR queries with preconfigured SQL. Archived November 2025. AWS recommends migrating to CUDOS.


5. LiteLLM Gateway — Attribution Architecture

When LiteLLM routes all traffic through one Bedrock IAM role, every CUR line item shows the same line_item_iam_principal. To recover per-team / per-project attribution:

Option A: STS AssumeRole per tenant (IAM principal attribution → CUR)

# LiteLLM custom auth / pre-call hook pattern
import boto3

def get_bedrock_client_for_tenant(tenant_id: str, team: str, project: str):
    sts = boto3.client('sts')
    response = sts.assume_role(
        RoleArn='arn:aws:iam::123456789012:role/bedrock-gateway-role',
        RoleSessionName=f'{tenant_id}',   # appears in line_item_iam_principal ARN
        Tags=[
            {'Key': 'team',    'Value': team},
            {'Key': 'project', 'Value': project},
            {'Key': 'env',     'Value': 'prod'},
        ]
    )
    creds = response['Credentials']
    return boto3.client(
        'bedrock-runtime',
        aws_access_key_id=creds['AccessKeyId'],
        aws_secret_access_key=creds['SecretAccessKey'],
        aws_session_token=creds['SessionToken'],
    )

Result in CUR 2.0:

  • line_item_iam_principal: arn:aws:sts::123456789012:assumed-role/bedrock-gateway-role/tenant-id
  • tags['iamPrincipal/team']: data-engineering
  • tags['iamPrincipal/project']: customer-360

Prerequisite: IAM role trust policy must include "sts:TagSession" action, or assume_role fails with AccessDenied.

Cache credentials: Credential TTL is 1 hour by default. Cache per tenant to avoid one STS call per request (STS call latency is ~50–200ms).

Option B: Request metadata per call (invocation logs only, not CUR)

# LiteLLM custom_llm_provider or pre-call hook
headers = {
    'X-Amzn-Bedrock-Request-Metadata': json.dumps({
        'team': team,
        'project': project,
        'user': user_id,
        'feature': feature_name,
    })
}

This appears in invocation logs as requestMetadata but does not appear in CUR. Use for per-prompt detail; use Option A for invoice-level attribution.

Option C: Application inference profiles per project (resource tag attribution → CUR)

Create one application inference profile ARN per project, tag it with project, team, use the profile ARN instead of the model ID. Tags flow into resourceTags/ in CUR 2.0.

Limit: ~1,000 profiles per account per region. Not suitable for high-cardinality attribution (per-user, per-session).


6. Known Gaps and Limitations

CUR data latency

  • Standard CUR 2.0 delivery: 24–48 hours after the usage day closes. Bedrock charges for a given day first appear in CUR the following day, sometimes 48 hours later.
  • IAM principal tag activation: Up to 24 hours after tag first appears; up to another 24 hours after activation before data populates.
  • Total new-tenant visibility lag: Up to 72–96 hours from first API call to first CUR row with iamPrincipal/team populated.
  • Cost Explorer: Same data as CUR; 24-hour lag typical.

Granularity

  • CUR aggregates by (usage_type, operation, identity/tag, hour or day). There is no requestId in CUR.
  • Finest grain available: per usage-type per day (or per hour with TIME_GRANULARITY = HOURLY).
  • Per-request detail requires invocation logs. Invocation logs are near real-time (seconds to S3).

What CUR does NOT contain

Missing from CUR Where to find it
Per-request requestId Invocation logs (requestId field)
Per-request input/output token counts Invocation logs (input.inputTokenCount, output.outputTokenCount)
Cache read/write token split at request level Invocation logs (if logged)
Latency per request Invocation logs or x-amzn-bedrock-invocation-latency response header
Prompt content / model input body Invocation logs (input.inputBodyJson, up to 100KB inline)
requestMetadata key-value tags Invocation logs (requestMetadata field)
Per-Converse-turn breakdown Not available; per-call only
Cross-region inference routing detail Usage type suffix (-cross-region-global) shows routing, not destination region

What invocation logs do NOT contain

Missing from invocation logs Where to find it
Dollar cost (invoice-accurate) CUR only
Discount/credit application CUR only (line_item_net_unblended_cost)
Savings Plan coverage CUR only (line_item_line_item_type = SavingsPlanCoveredUsage)
Bedrock Mantle endpoint calls Not supported by invocation logging (as of mid-2026)

IAM principal attribution scope

  • Only available for bedrock-runtime endpoint (InvokeModel, Converse, Chat Completions).
  • Not available for bedrock-mantle endpoint (Responses API, Anthropic Messages). For mantle, use Projects/Workspaces.
  • INCLUDE_IAM_PRINCIPAL_DATA only adds data for Bedrock costs; other services are unaffected.

File size impact

Enabling INCLUDE_IAM_PRINCIPAL_DATA = TRUE expands each aggregated Bedrock line item into N rows, one per calling IAM identity. For an account with 50 IAM roles calling Bedrock, this multiplies Bedrock-related CUR row count by ~50. Plan S3 storage and Athena scan cost accordingly.


7. Attribution Method Decision Matrix

Goal Mechanism CUR column Granularity Latency
Per-team dollar chargeback IAM principal tags via STS + INCLUDE_IAM_PRINCIPAL_DATA tags['iamPrincipal/team'] Day (aggregated) 24–48h
Per-project dollar chargeback Application inference profiles + resource tags tags['resourceTags/project'] Day (aggregated) 24–48h
Per-user dollar chargeback at scale STS RoleSessionName per user line_item_iam_principal (session in ARN) Day (aggregated) 24–48h
Per-prompt token count (any dimension) Request metadata (X-Amzn-Bedrock-Request-Metadata) + invocation logs Not in CUR — invocation logs only Per-request Seconds
Invoice reconciliation Join invocation logs to CUR at model+day grain Both Day 24–48h
Cross-region inference cost CUR line_item_usage_type LIKE '%-cross-region-global' line_item_usage_type Day 24–48h

8. Invocation Log Schema (Athena CREATE TABLE)

CREATE EXTERNAL TABLE bedrocklogsdb.bedrocklogs (
    schematype          STRING,
    schemaversion       STRING,
    timestamp           STRING,
    accountid           STRING,
    region              STRING,
    requestid           STRING,
    operation           STRING,
    modelid             STRING,
    identity STRUCT<
        arn: STRING
    >,
    requestmetadata     MAP<STRING, STRING>,
    input STRUCT<
        inputcontenttype: STRING,
        inputtokencount:  INT,
        inputbodyjson:    STRING
    >,
    output STRUCT<
        outputcontenttype: STRING,
        outputtokencount:  INT,
        outputbodyjson:    STRING
    >
)
PARTITIONED BY (dt STRING, h STRING)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES ('ignore.malformed.json' = 'TRUE')
STORED AS INPUTFORMAT  'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT           'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://YOUR-BUCKET/YOUR-PREFIX/AWSLogs/123456789012/BedrockModelInvocationLogs/us-east-1/'
TBLPROPERTIES ('projection.enabled' = 'true',
               'projection.dt.type' = 'date',
               'projection.dt.format' = 'yyyy/MM/dd',
               'projection.dt.range' = '2026/01/01,NOW',
               'projection.h.type' = 'integer',
               'projection.h.range' = '0,23',
               'storage.location.template' =
                   's3://YOUR-BUCKET/YOUR-PREFIX/AWSLogs/123456789012/BedrockModelInvocationLogs/us-east-1/${dt}/${h}/');

9. Source Ledger

Claim Source Strength
CUR 2.0 Bedrock column names, line_item_usage_type patterns AWS docs: Understanding your Amazon Bedrock CUR data Official docs verified
INCLUDE_IAM_PRINCIPAL_DATA config key, data starts Apr 8 2026 AWS docs: CUR 2.0 table dictionary Official docs verified
line_item_iam_principal column, IAM ARN formats AWS docs: Using IAM principal for cost allocation Official docs verified
iamPrincipal/ tag prefix, tag prefix taxonomy AWS docs: CUR 2.0 Tags Column Official docs verified
Per-request metadata, invocation log schema, requestMetadata field AWS docs: Per-request metadata tagging Official docs verified
Invocation log schema, inputTokenCount, outputTokenCount field names AWS docs: Model invocation logging Official docs verified
Attribution method comparison table (CUR vs invocation logs) AWS docs: Track usage and costs in Bedrock Official docs verified
InvokeModelInference / InvokeModelStreamingInference operation values (Jan 2026) AWS What’s New: Bedrock granular operation visibility Official docs verified
CUR 2.0 fixed 125-column schema, new columns vs legacy CUR AWS docs: CUR 2.0 table dictionary Official docs verified
CUDOS Bedrock visuals by version, cost per 1M tokens CUDOS CHANGELOG Source/code verified
aws-usage-queries archived Nov 2025 GitHub: aws-samples/aws-usage-queries Source/code verified
STS TagSession requirement for session tags AWS guidance: guidance-for-claude-code-with-amazon-bedrock COST_ATTRIBUTION.md Official docs verified
Application inference profile ~1,000/account limit FinOut blog: Hidden Superpower of Bedrock Cost Allocation Community pattern

10. Next Research Lanes

  1. Bedrock Savings Plans and CUR — how SavingsPlanCoveredUsage line items interact with IAM principal attribution; whether RI/SP discounts are visible per-principal or only at the aggregate level.

  2. bedrock-mantle endpoint FinOps gap — Projects and Workspaces handle attribution for Responses API / Anthropic Messages, but there is no invocation logging for mantle. This is a blind spot for Anthropic-SDK-native workloads.

  3. Multi-account aggregation — how management-account CUR consolidates Bedrock spend across member accounts; whether line_item_iam_principal works cross-account via resource-based policies.

  4. LiteLLM session credential caching — validate whether LiteLLM’s built-in AWS credential provider supports per-virtual-key STS AssumeRole + tag injection, or whether a custom pre-call hook is required.

  5. Athena partition projection for CUR 2.0 — CUR 2.0 uses billing_period as the partition key (not year/month as in legacy CUR); confirm the correct partition projection config for the Data Exports S3 layout.