From f0863ec855cd04385f10ec7689d67c7c8d364036 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 1 Oct 2026 20:35:09 -0700 Subject: [PATCH 1/3] feat(attributes): Add `vercel.ai.telemetry.metadata.` attribute The Vercel AI integration in sentry-javascript records the `experimental_telemetry.metadata` of an AI SDK call as `vercel.ai.telemetry.metadata.` span attributes. SDK v10 did this through the AI SDK's own OTel spans. A fix in v11 restores it for `ai` 4 to 6 (getsentry/sentry-javascript#24721). The type is `any`. The AI SDK allows strings, numbers, booleans and arrays of these as metadata values, and the SDK sends them unchanged. The schema allows only one type per attribute, so `any` is the only type that covers all of these values. The docs content schema did not accept `any`, though `schemas/attribute.schema.json` allows it, as of #273. No attribute used `any` before. Related: https://github.com/getsentry/sentry-javascript/pull/24721 Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/content.config.ts | 2 +- .../sentry-conventions/src/attributes.ts | 51 +++++++++++++++++++ javascript/sentry-conventions/src/search.ts | 13 +++++ ...ercel__ai__telemetry__metadata__[key].json | 24 +++++++++ python/src/sentry_conventions/attributes.py | 40 +++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts index b02ef3aa3..00162ac3e 100644 --- a/docs/src/content.config.ts +++ b/docs/src/content.config.ts @@ -17,7 +17,7 @@ const attributeSchema = z key: z.string(), brief: z.string(), has_dynamic_suffix: z.boolean().optional(), - type: z.enum(['string', 'boolean', 'integer', 'double', 'string[]', 'boolean[]', 'integer[]', 'double[]']), + type: z.enum(['string', 'boolean', 'integer', 'double', 'string[]', 'boolean[]', 'integer[]', 'double[]', 'any']), apply_scrubbing: z.object({ key: z.enum(['auto', 'manual', 'never']), reason: z.string().optional(), diff --git a/javascript/sentry-conventions/src/attributes.ts b/javascript/sentry-conventions/src/attributes.ts index 383bb4b36..3838123d9 100644 --- a/javascript/sentry-conventions/src/attributes.ts +++ b/javascript/sentry-conventions/src/attributes.ts @@ -18658,6 +18658,34 @@ export const USER_ROLES = 'user.roles'; */ export type USER_ROLES_TYPE = Array; +// Path: model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json + +/** + * Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name. `vercel.ai.telemetry.metadata.` + * + * Attribute Value Type: `unknown` {@link VERCEL_AI_TELEMETRY_METADATA_KEY_TYPE} + * + * Apply Scrubbing: auto - Metadata is user-defined and can contain user data + * + * Attribute defined in OTEL: No + * Visibility: public + * + * Has Dynamic Suffix: true + * + * @example "vercel.ai.telemetry.metadata.tenantId='acme'" + */ +export const VERCEL_AI_TELEMETRY_METADATA_KEY = 'vercel.ai.telemetry.metadata.'; + +/** + * Base key for {@link VERCEL_AI_TELEMETRY_METADATA_KEY}. Use with a dynamic suffix, e.g. `${VERCEL_AI_TELEMETRY_METADATA_KEY_BASE}.${key}`. + */ +export const VERCEL_AI_TELEMETRY_METADATA_KEY_BASE = 'vercel.ai.telemetry.metadata'; + +/** + * Type for {@link VERCEL_AI_TELEMETRY_METADATA_KEY} vercel.ai.telemetry.metadata. + */ +export type VERCEL_AI_TELEMETRY_METADATA_KEY_TYPE = unknown; + // Path: model/attributes/vercel/vercel__branch.json /** @@ -20342,6 +20370,7 @@ export const ATTRIBUTE_TYPE: Record = { 'user.ip_address': 'string', 'user.name': 'string', 'user.roles': 'string[]', + 'vercel.ai.telemetry.metadata.': 'any', 'vercel.branch': 'string', 'vercel.build_id': 'string', 'vercel.deployment_id': 'string', @@ -21213,6 +21242,7 @@ export type AttributeName = | typeof USER_IP_ADDRESS | typeof USER_NAME | typeof USER_ROLES + | typeof VERCEL_AI_TELEMETRY_METADATA_KEY | typeof VERCEL_BRANCH | typeof VERCEL_BUILD_ID | typeof VERCEL_DEPLOYMENT_ID @@ -34819,6 +34849,26 @@ export const ATTRIBUTE_METADATA: Record = { example: ['admin', 'editor'], changelog: [{ version: '0.0.0' }], }, + 'vercel.ai.telemetry.metadata.': { + brief: + 'Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.', + type: 'any', + keys: ['vercel.ai.telemetry.metadata.'], + applyScrubbing: { + key: 'auto', + reason: 'Metadata is user-defined and can contain user data', + }, + isInOtel: false, + visibility: 'public', + hasDynamicSuffix: true, + example: "vercel.ai.telemetry.metadata.tenantId='acme'", + examples: ["vercel.ai.telemetry.metadata.tenantId='acme'"], + changelog: [{ version: 'next', prs: [662], description: 'Added vercel.ai.telemetry.metadata. attribute' }], + additionalContext: [ + 'Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.', + "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", + ], + }, 'vercel.branch': { brief: 'Git branch name for Vercel project', type: 'string', @@ -36100,6 +36150,7 @@ export type Attributes = { [USER_IP_ADDRESS]?: USER_IP_ADDRESS_TYPE; [USER_NAME]?: USER_NAME_TYPE; [USER_ROLES]?: USER_ROLES_TYPE; + [VERCEL_AI_TELEMETRY_METADATA_KEY]?: VERCEL_AI_TELEMETRY_METADATA_KEY_TYPE; [VERCEL_BRANCH]?: VERCEL_BRANCH_TYPE; [VERCEL_BUILD_ID]?: VERCEL_BUILD_ID_TYPE; [VERCEL_DEPLOYMENT_ID]?: VERCEL_DEPLOYMENT_ID_TYPE; diff --git a/javascript/sentry-conventions/src/search.ts b/javascript/sentry-conventions/src/search.ts index e9ef2e9bb..467715f89 100644 --- a/javascript/sentry-conventions/src/search.ts +++ b/javascript/sentry-conventions/src/search.ts @@ -4850,6 +4850,11 @@ export const SEARCH_USER__USERNAME = 'user.username'; */ export const SEARCH_USER_AGENT__ORIGINAL = 'user_agent.original'; +/** + * Search name for {@link attributes.VERCEL_AI_TELEMETRY_METADATA_KEY}. `vercel.ai.telemetry.metadata.` + */ +export const SEARCH_VERCEL__AI__TELEMETRY__METADATA__KEY = 'vercel.ai.telemetry.metadata.'; + /** * Search name for {@link attributes.VERCEL_BRANCH}. `vercel.branch` */ @@ -5884,6 +5889,7 @@ export type AttributeSearchName = | typeof SEARCH_USER__ROLES | typeof SEARCH_USER__USERNAME | typeof SEARCH_USER_AGENT__ORIGINAL + | typeof SEARCH_VERCEL__AI__TELEMETRY__METADATA__KEY | typeof SEARCH_VERCEL__BRANCH | typeof SEARCH_VERCEL__BUILD_ID | typeof SEARCH_VERCEL__DEPLOYMENT_ID @@ -11421,6 +11427,13 @@ export const ATTRIBUTE_SEARCH_METADATA: Record brief: 'Value of the HTTP User-Agent header sent by the client.', deprecationChain: ['user_agent.original', 'http.user_agent'], }, + 'vercel.ai.telemetry.metadata.': { + canonicalName: 'vercel.ai.telemetry.metadata.', + type: 'any', + brief: + 'Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.', + deprecationChain: ['vercel.ai.telemetry.metadata.'], + }, 'vercel.branch': { canonicalName: 'vercel.branch', type: 'string', diff --git a/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json b/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json new file mode 100644 index 000000000..e76fc5acb --- /dev/null +++ b/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json @@ -0,0 +1,24 @@ +{ + "key": "vercel.ai.telemetry.metadata.", + "brief": "Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.", + "has_dynamic_suffix": true, + "type": "any", + "apply_scrubbing": { + "key": "auto", + "reason": "Metadata is user-defined and can contain user data" + }, + "additional_context": [ + "Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.", + "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`." + ], + "is_in_otel": false, + "visibility": "public", + "examples": ["vercel.ai.telemetry.metadata.tenantId='acme'"], + "changelog": [ + { + "version": "next", + "prs": [662], + "description": "Added vercel.ai.telemetry.metadata. attribute" + } + ] +} diff --git a/python/src/sentry_conventions/attributes.py b/python/src/sentry_conventions/attributes.py index 84dd5c22f..4e735c7ec 100644 --- a/python/src/sentry_conventions/attributes.py +++ b/python/src/sentry_conventions/attributes.py @@ -10898,6 +10898,20 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): Example: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1" """ + # Path: model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json + VERCEL_AI_TELEMETRY_METADATA_KEY: Literal["vercel.ai.telemetry.metadata."] = ( + "vercel.ai.telemetry.metadata." + ) + """Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name. + + Type: object + Apply Scrubbing: auto - Metadata is user-defined and can contain user data + Defined in OTEL: No + Visibility: public + Has Dynamic Suffix: true + Example: "vercel.ai.telemetry.metadata.tenantId='acme'" + """ + # Path: model/attributes/vercel/vercel__branch.json VERCEL_BRANCH: Literal["vercel.branch"] = "vercel.branch" """Git branch name for Vercel project @@ -27244,6 +27258,31 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): ChangelogEntry(version="0.0.0"), ], ), + "vercel.ai.telemetry.metadata.": AttributeMetadata( + brief="Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.", + type=AttributeType.ANY, + keys=("vercel.ai.telemetry.metadata.",), + apply_scrubbing=ApplyScrubbingInfo( + key=ApplyScrubbing.AUTO, + reason="Metadata is user-defined and can contain user data", + ), + is_in_otel=False, + visibility=Visibility.PUBLIC, + has_dynamic_suffix=True, + example="vercel.ai.telemetry.metadata.tenantId='acme'", + examples=["vercel.ai.telemetry.metadata.tenantId='acme'"], + changelog=[ + ChangelogEntry( + version="next", + prs=[662], + description="Added vercel.ai.telemetry.metadata. attribute", + ), + ], + additional_context=[ + "Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.", + "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", + ], + ), "vercel.branch": AttributeMetadata( brief="Git branch name for Vercel project", type=AttributeType.STRING, @@ -28524,6 +28563,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): "user.name": str, "user.roles": List[str], "user_agent.original": str, + "vercel.ai.telemetry.metadata.": object, "vercel.branch": str, "vercel.build_id": str, "vercel.deployment_id": str, From 19fbd7dfd312ccf9a916f73e55344c5d8b11360e Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 5 Oct 2026 07:26:10 -0700 Subject: [PATCH 2/3] fix: add note re-openness If vercel ever does make it more strict, we can fold it in somewhere else. --- javascript/sentry-conventions/src/attributes.ts | 1 + .../vercel/vercel__ai__telemetry__metadata__[key].json | 3 ++- python/src/sentry_conventions/attributes.py | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/javascript/sentry-conventions/src/attributes.ts b/javascript/sentry-conventions/src/attributes.ts index 3838123d9..3869de4fa 100644 --- a/javascript/sentry-conventions/src/attributes.ts +++ b/javascript/sentry-conventions/src/attributes.ts @@ -34867,6 +34867,7 @@ export const ATTRIBUTE_METADATA: Record = { additionalContext: [ 'Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.', "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", + 'This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys or restrict the values beyond primitive types and arrays. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes.', ], }, 'vercel.branch': { diff --git a/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json b/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json index e76fc5acb..5e2efd183 100644 --- a/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json +++ b/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json @@ -9,7 +9,8 @@ }, "additional_context": [ "Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.", - "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`." + "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", + "This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys or restrict the values beyond primitive types and arrays. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes." ], "is_in_otel": false, "visibility": "public", diff --git a/python/src/sentry_conventions/attributes.py b/python/src/sentry_conventions/attributes.py index 4e735c7ec..7b7a9195b 100644 --- a/python/src/sentry_conventions/attributes.py +++ b/python/src/sentry_conventions/attributes.py @@ -27281,6 +27281,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): additional_context=[ "Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.", "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", + "This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys or restrict the values beyond primitive types and arrays. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes.", ], ), "vercel.branch": AttributeMetadata( From 76d52b8558f507b7aadd0c5162f9a2eabbfc9404 Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 5 Oct 2026 09:22:21 -0700 Subject: [PATCH 3/3] fix: cast vercelai.metadata.* fields to string --- javascript/sentry-conventions/src/attributes.ts | 11 ++++++----- javascript/sentry-conventions/src/search.ts | 2 +- .../vercel__ai__telemetry__metadata__[key].json | 5 +++-- python/src/sentry_conventions/attributes.py | 9 +++++---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/javascript/sentry-conventions/src/attributes.ts b/javascript/sentry-conventions/src/attributes.ts index 3869de4fa..74117834b 100644 --- a/javascript/sentry-conventions/src/attributes.ts +++ b/javascript/sentry-conventions/src/attributes.ts @@ -18663,7 +18663,7 @@ export type USER_ROLES_TYPE = Array; /** * Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name. `vercel.ai.telemetry.metadata.` * - * Attribute Value Type: `unknown` {@link VERCEL_AI_TELEMETRY_METADATA_KEY_TYPE} + * Attribute Value Type: `string` {@link VERCEL_AI_TELEMETRY_METADATA_KEY_TYPE} * * Apply Scrubbing: auto - Metadata is user-defined and can contain user data * @@ -18684,7 +18684,7 @@ export const VERCEL_AI_TELEMETRY_METADATA_KEY_BASE = 'vercel.ai.telemetry.metada /** * Type for {@link VERCEL_AI_TELEMETRY_METADATA_KEY} vercel.ai.telemetry.metadata. */ -export type VERCEL_AI_TELEMETRY_METADATA_KEY_TYPE = unknown; +export type VERCEL_AI_TELEMETRY_METADATA_KEY_TYPE = string; // Path: model/attributes/vercel/vercel__branch.json @@ -20370,7 +20370,7 @@ export const ATTRIBUTE_TYPE: Record = { 'user.ip_address': 'string', 'user.name': 'string', 'user.roles': 'string[]', - 'vercel.ai.telemetry.metadata.': 'any', + 'vercel.ai.telemetry.metadata.': 'string', 'vercel.branch': 'string', 'vercel.build_id': 'string', 'vercel.deployment_id': 'string', @@ -34852,7 +34852,7 @@ export const ATTRIBUTE_METADATA: Record = { 'vercel.ai.telemetry.metadata.': { brief: 'Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.', - type: 'any', + type: 'string', keys: ['vercel.ai.telemetry.metadata.'], applyScrubbing: { key: 'auto', @@ -34867,7 +34867,8 @@ export const ATTRIBUTE_METADATA: Record = { additionalContext: [ 'Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.', "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", - 'This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys or restrict the values beyond primitive types and arrays. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes.', + 'The Vercel AI SDK also allows numbers, booleans and arrays as metadata values. SDKs MUST convert these values to strings.', + 'This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes.', ], }, 'vercel.branch': { diff --git a/javascript/sentry-conventions/src/search.ts b/javascript/sentry-conventions/src/search.ts index 467715f89..85994ff17 100644 --- a/javascript/sentry-conventions/src/search.ts +++ b/javascript/sentry-conventions/src/search.ts @@ -11429,7 +11429,7 @@ export const ATTRIBUTE_SEARCH_METADATA: Record }, 'vercel.ai.telemetry.metadata.': { canonicalName: 'vercel.ai.telemetry.metadata.', - type: 'any', + type: 'string', brief: 'Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.', deprecationChain: ['vercel.ai.telemetry.metadata.'], diff --git a/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json b/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json index 5e2efd183..55013f0a5 100644 --- a/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json +++ b/model/attributes/vercel/vercel__ai__telemetry__metadata__[key].json @@ -2,7 +2,7 @@ "key": "vercel.ai.telemetry.metadata.", "brief": "Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.", "has_dynamic_suffix": true, - "type": "any", + "type": "string", "apply_scrubbing": { "key": "auto", "reason": "Metadata is user-defined and can contain user data" @@ -10,7 +10,8 @@ "additional_context": [ "Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.", "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", - "This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys or restrict the values beyond primitive types and arrays. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes." + "The Vercel AI SDK also allows numbers, booleans and arrays as metadata values. SDKs MUST convert these values to strings.", + "This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes." ], "is_in_otel": false, "visibility": "public", diff --git a/python/src/sentry_conventions/attributes.py b/python/src/sentry_conventions/attributes.py index 7b7a9195b..f2a2733d3 100644 --- a/python/src/sentry_conventions/attributes.py +++ b/python/src/sentry_conventions/attributes.py @@ -10904,7 +10904,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): ) """Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name. - Type: object + Type: str Apply Scrubbing: auto - Metadata is user-defined and can contain user data Defined in OTEL: No Visibility: public @@ -27260,7 +27260,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): ), "vercel.ai.telemetry.metadata.": AttributeMetadata( brief="Metadata passed to a Vercel AI SDK call through `experimental_telemetry.metadata`, with being the metadata key name.", - type=AttributeType.ANY, + type=AttributeType.STRING, keys=("vercel.ai.telemetry.metadata.",), apply_scrubbing=ApplyScrubbingInfo( key=ApplyScrubbing.AUTO, @@ -27281,7 +27281,8 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): additional_context=[ "Set on `gen_ai` spans for `invoke_agent`, `generate_content` and `embeddings` operations. Not set on `execute_tool` spans.", "The Vercel AI SDK's own OpenTelemetry spans emit the same data as `ai.telemetry.metadata.`.", - "This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys or restrict the values beyond primitive types and arrays. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes.", + "The Vercel AI SDK also allows numbers, booleans and arrays as metadata values. SDKs MUST convert these values to strings.", + "This attribute is open-ended on purpose. The Vercel AI SDK does not define the metadata keys. If Vercel defines a stricter spec for this metadata, consider moving it into `gen_ai.*` attributes.", ], ), "vercel.branch": AttributeMetadata( @@ -28564,7 +28565,7 @@ class ATTRIBUTE_NAMES(metaclass=_AttributeNamesMeta): "user.name": str, "user.roles": List[str], "user_agent.original": str, - "vercel.ai.telemetry.metadata.": object, + "vercel.ai.telemetry.metadata.": str, "vercel.branch": str, "vercel.build_id": str, "vercel.deployment_id": str,