{"openapi":"3.0.0","info":{"title":"Ours Privacy REST API","version":"1.0.0","description":"REST API for programmatic configuration management. Authenticated with Ours Privacy API keys."},"servers":[{"url":"https://app.oursprivacy.com"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Ours Privacy API key"}},"schemas":{},"parameters":{}},"paths":{"/rest/v1/destinations":{"get":{"operationId":"listDestinations","tags":["Destinations"],"x-stainless-method":"destinations.list","security":[{"apiKey":[]}],"description":"List all destinations. Requires scope: destination:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["Disabled","Enabled"],"description":"Filter destinations by status."},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["AWSEventBridge","AWSKinesis","AWSLambda","AWSS3","AWSSNS","ActiveCampaignApi","Admitad","AdobeAnalytics","AmazonDSP","Amplitude","AppLovin","ArtsAI","Attentive","Audiohook","AzureBlob","BasisPostback","BeeswaxPostback","BingAds","BingAdsWeb","Braze","ConvertABTestingEvent","Customerio","DatabricksWarehouse","DomoWarehouse","Everflow","Facebook","FacebookAudience","FloodlightSGTM","FullContact","Fullstory","G4Analytics","GA4MeasurementProtocol","GA4ServerProxy","Google","GoogleAds360","GoogleAdsServerContainer","GoogleAudience","GoogleBigQuery","GoogleBigQueryWarehouse","GoogleDataManagerEventIngest","GooglePubSub","GoogleStorage","HTTPCustomRequest","HTTPDestination","Hubspot","IHeartMediaMagellan","Impact","Iterable","Klaviyo","LinkedInAdsCAPI","LiveIntent","LiveRampWarehouse","MNTN","MNTNAudience","Mailchimp","MicrosoftCAPI","Mixpanel","NextdoorAds","OpenAIAds","OursSyntheticData","Outbrain","Partnerize","Pinterest","Plausible","Podscribe","PostHog","QuantcastCAPI","QuoraAds","Reddit","Rokt","RokuCAPI","SnapchatAdsCapi","Spotify","StackAdaptAPI","Taboola","Tatari","TatariCAPI","TheTradeDesk","TikTok","UniversalAds","VWO","Viant","ViantCAPI","Vibe","VibeAudience","Woopra","XAds","YelpCAPI","Zendesk","ZohoCRM","ZoomInfo"],"description":"Filter destinations by destination type."},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"List of destinations","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AWSEventBridge","AWSKinesis","AWSLambda","AWSS3","AWSSNS","ActiveCampaignApi","Admitad","AdobeAnalytics","AmazonDSP","Amplitude","AppLovin","ArtsAI","Attentive","Audiohook","AzureBlob","BasisPostback","BeeswaxPostback","BingAds","BingAdsWeb","Braze","ConvertABTestingEvent","Customerio","DatabricksWarehouse","DomoWarehouse","Everflow","Facebook","FacebookAudience","FloodlightSGTM","FullContact","Fullstory","G4Analytics","GA4MeasurementProtocol","GA4ServerProxy","Google","GoogleAds360","GoogleAdsServerContainer","GoogleAudience","GoogleBigQuery","GoogleBigQueryWarehouse","GoogleDataManagerEventIngest","GooglePubSub","GoogleStorage","HTTPCustomRequest","HTTPDestination","Hubspot","IHeartMediaMagellan","Impact","Iterable","Klaviyo","LinkedInAdsCAPI","LiveIntent","LiveRampWarehouse","MNTN","MNTNAudience","Mailchimp","MicrosoftCAPI","Mixpanel","NextdoorAds","OpenAIAds","OursSyntheticData","Outbrain","Partnerize","Pinterest","Plausible","Podscribe","PostHog","QuantcastCAPI","QuoraAds","Reddit","Rokt","RokuCAPI","SnapchatAdsCapi","Spotify","StackAdaptAPI","Taboola","Tatari","TatariCAPI","TheTradeDesk","TikTok","UniversalAds","VWO","Viant","ViantCAPI","Vibe","VibeAudience","Woopra","XAds","YelpCAPI","Zendesk","ZohoCRM","ZoomInfo"],"description":"Destination type. Read responses may include warehouse, cloud-storage, and audience-sync types that are not creatable through POST /rest/v1/destinations."},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"settings":{"type":"object","nullable":true},"limitedToSourceIds":{"type":"array","nullable":true,"items":{"type":"string"}},"hashingSalt":{"type":"string","nullable":true}},"required":["id","type","status","createdAt"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createDestinations","tags":["Destinations"],"x-stainless-method":"destinations.create","security":[{"apiKey":[]}],"description":"Create a new destination. Requires scope: destination:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["Audiohook","BasisPostback","Outbrain","OursSyntheticData","FullContact","ZoomInfo","TheTradeDesk","Braze","LiveIntent","ConvertABTestingEvent","Customerio","BingAds","BingAdsWeb","MicrosoftCAPI","HTTPDestination","Woopra","HTTPCustomRequest","Google","GoogleAdsServerContainer","G4Analytics","GA4ServerProxy","GA4MeasurementProtocol","GoogleAds360","Facebook","Mixpanel","Amplitude","TikTok","Reddit","Podscribe","Pinterest","Mailchimp","AWSKinesis","AWSLambda","AWSSNS","GooglePubSub","LinkedInAdsCAPI","ActiveCampaignApi","StackAdaptAPI","Hubspot","Klaviyo","XAds","QuoraAds","SnapchatAdsCapi","Partnerize","NextdoorAds","Tatari","TatariCAPI","Viant","ViantCAPI","Impact","Spotify","Taboola","AmazonDSP","AppLovin","IHeartMediaMagellan","Vibe","GoogleDataManagerEventIngest","Zendesk","Iterable","ArtsAI","QuantcastCAPI","FloodlightSGTM","VWO","Attentive","Admitad","Plausible","PostHog","RokuCAPI","Everflow","BeeswaxPostback","AdobeAnalytics","UniversalAds","OpenAIAds","YelpCAPI","MNTN","ZohoCRM","Rokt","Fullstory"],"description":"Event-dispatch destination type to create. Warehouse, cloud-storage, and audience-sync destination types may appear on read responses but are not creatable through POST. Audience destinations receive audience membership from Audience Builder on a daily schedule and are configured in the dashboard."},"settings":{"type":"object","description":"Per-type configuration keys and values. Call GET /rest/v1/destinations/types to get the valid keys for your destination type.","nullable":true}},"required":["type"]}}}},"responses":{"200":{"description":"Created destination","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AWSEventBridge","AWSKinesis","AWSLambda","AWSS3","AWSSNS","ActiveCampaignApi","Admitad","AdobeAnalytics","AmazonDSP","Amplitude","AppLovin","ArtsAI","Attentive","Audiohook","AzureBlob","BasisPostback","BeeswaxPostback","BingAds","BingAdsWeb","Braze","ConvertABTestingEvent","Customerio","DatabricksWarehouse","DomoWarehouse","Everflow","Facebook","FacebookAudience","FloodlightSGTM","FullContact","Fullstory","G4Analytics","GA4MeasurementProtocol","GA4ServerProxy","Google","GoogleAds360","GoogleAdsServerContainer","GoogleAudience","GoogleBigQuery","GoogleBigQueryWarehouse","GoogleDataManagerEventIngest","GooglePubSub","GoogleStorage","HTTPCustomRequest","HTTPDestination","Hubspot","IHeartMediaMagellan","Impact","Iterable","Klaviyo","LinkedInAdsCAPI","LiveIntent","LiveRampWarehouse","MNTN","MNTNAudience","Mailchimp","MicrosoftCAPI","Mixpanel","NextdoorAds","OpenAIAds","OursSyntheticData","Outbrain","Partnerize","Pinterest","Plausible","Podscribe","PostHog","QuantcastCAPI","QuoraAds","Reddit","Rokt","RokuCAPI","SnapchatAdsCapi","Spotify","StackAdaptAPI","Taboola","Tatari","TatariCAPI","TheTradeDesk","TikTok","UniversalAds","VWO","Viant","ViantCAPI","Vibe","VibeAudience","Woopra","XAds","YelpCAPI","Zendesk","ZohoCRM","ZoomInfo"],"description":"Destination type. Read responses may include warehouse, cloud-storage, and audience-sync types that are not creatable through POST /rest/v1/destinations."},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"settings":{"type":"object","nullable":true},"limitedToSourceIds":{"type":"array","nullable":true,"items":{"type":"string"}},"hashingSalt":{"type":"string","nullable":true}},"required":["id","type","status","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/destinations/{id}":{"get":{"operationId":"findDestinations","tags":["Destinations"],"x-stainless-method":"destinations.retrieve","security":[{"apiKey":[]}],"description":"Find a single destination by ID. Requires scope: destination:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Destination details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AWSEventBridge","AWSKinesis","AWSLambda","AWSS3","AWSSNS","ActiveCampaignApi","Admitad","AdobeAnalytics","AmazonDSP","Amplitude","AppLovin","ArtsAI","Attentive","Audiohook","AzureBlob","BasisPostback","BeeswaxPostback","BingAds","BingAdsWeb","Braze","ConvertABTestingEvent","Customerio","DatabricksWarehouse","DomoWarehouse","Everflow","Facebook","FacebookAudience","FloodlightSGTM","FullContact","Fullstory","G4Analytics","GA4MeasurementProtocol","GA4ServerProxy","Google","GoogleAds360","GoogleAdsServerContainer","GoogleAudience","GoogleBigQuery","GoogleBigQueryWarehouse","GoogleDataManagerEventIngest","GooglePubSub","GoogleStorage","HTTPCustomRequest","HTTPDestination","Hubspot","IHeartMediaMagellan","Impact","Iterable","Klaviyo","LinkedInAdsCAPI","LiveIntent","LiveRampWarehouse","MNTN","MNTNAudience","Mailchimp","MicrosoftCAPI","Mixpanel","NextdoorAds","OpenAIAds","OursSyntheticData","Outbrain","Partnerize","Pinterest","Plausible","Podscribe","PostHog","QuantcastCAPI","QuoraAds","Reddit","Rokt","RokuCAPI","SnapchatAdsCapi","Spotify","StackAdaptAPI","Taboola","Tatari","TatariCAPI","TheTradeDesk","TikTok","UniversalAds","VWO","Viant","ViantCAPI","Vibe","VibeAudience","Woopra","XAds","YelpCAPI","Zendesk","ZohoCRM","ZoomInfo"],"description":"Destination type. Read responses may include warehouse, cloud-storage, and audience-sync types that are not creatable through POST /rest/v1/destinations."},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"settings":{"type":"object","nullable":true},"limitedToSourceIds":{"type":"array","nullable":true,"items":{"type":"string"}},"hashingSalt":{"type":"string","nullable":true}},"required":["id","type","status","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchDestinations","tags":["Destinations"],"x-stainless-method":"destinations.update","security":[{"apiKey":[]}],"description":"Partially update a destination. Only the fields you send are changed; omitted fields are unchanged. The `settings` object is patch-only: omitted keys keep their current value, and send `null` to clear a specific setting. Requires scope: destination:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned read-only field. Accepted on PATCH and ignored to support GET -> PATCH round-trips.","readOnly":true},"createdAt":{"type":"string","description":"Server-assigned read-only field. Accepted on PATCH and ignored to support GET -> PATCH round-trips.","readOnly":true},"updatedAt":{"type":"string","description":"Server-assigned read-only field. Accepted on PATCH and ignored to support GET -> PATCH round-trips.","nullable":true,"readOnly":true},"type":{"type":"string","enum":["AWSEventBridge","AWSKinesis","AWSLambda","AWSS3","AWSSNS","ActiveCampaignApi","Admitad","AdobeAnalytics","AmazonDSP","Amplitude","AppLovin","ArtsAI","Attentive","Audiohook","AzureBlob","BasisPostback","BeeswaxPostback","BingAds","BingAdsWeb","Braze","ConvertABTestingEvent","Customerio","DatabricksWarehouse","DomoWarehouse","Everflow","Facebook","FacebookAudience","FloodlightSGTM","FullContact","Fullstory","G4Analytics","GA4MeasurementProtocol","GA4ServerProxy","Google","GoogleAds360","GoogleAdsServerContainer","GoogleAudience","GoogleBigQuery","GoogleBigQueryWarehouse","GoogleDataManagerEventIngest","GooglePubSub","GoogleStorage","HTTPCustomRequest","HTTPDestination","Hubspot","IHeartMediaMagellan","Impact","Iterable","Klaviyo","LinkedInAdsCAPI","LiveIntent","LiveRampWarehouse","MNTN","MNTNAudience","Mailchimp","MicrosoftCAPI","Mixpanel","NextdoorAds","OpenAIAds","OursSyntheticData","Outbrain","Partnerize","Pinterest","Plausible","Podscribe","PostHog","QuantcastCAPI","QuoraAds","Reddit","Rokt","RokuCAPI","SnapchatAdsCapi","Spotify","StackAdaptAPI","Taboola","Tatari","TatariCAPI","TheTradeDesk","TikTok","UniversalAds","VWO","Viant","ViantCAPI","Vibe","VibeAudience","Woopra","XAds","YelpCAPI","Zendesk","ZohoCRM","ZoomInfo"],"description":"Server-assigned read-only field. Accepted on PATCH and ignored to support GET -> PATCH round-trips.","readOnly":true},"status":{"type":"string","nullable":true,"enum":["Disabled","Enabled"]},"name":{"type":"string","nullable":true},"hashingSalt":{"type":"string","nullable":true},"limitedToSourceIds":{"type":"array","nullable":true,"items":{"type":"string"}},"settings":{"type":"object","description":"Per-type configuration keys and values. Call GET /rest/v1/destinations/types to get the valid keys for your destination type.","nullable":true}}}}}},"responses":{"200":{"description":"Updated destination","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AWSEventBridge","AWSKinesis","AWSLambda","AWSS3","AWSSNS","ActiveCampaignApi","Admitad","AdobeAnalytics","AmazonDSP","Amplitude","AppLovin","ArtsAI","Attentive","Audiohook","AzureBlob","BasisPostback","BeeswaxPostback","BingAds","BingAdsWeb","Braze","ConvertABTestingEvent","Customerio","DatabricksWarehouse","DomoWarehouse","Everflow","Facebook","FacebookAudience","FloodlightSGTM","FullContact","Fullstory","G4Analytics","GA4MeasurementProtocol","GA4ServerProxy","Google","GoogleAds360","GoogleAdsServerContainer","GoogleAudience","GoogleBigQuery","GoogleBigQueryWarehouse","GoogleDataManagerEventIngest","GooglePubSub","GoogleStorage","HTTPCustomRequest","HTTPDestination","Hubspot","IHeartMediaMagellan","Impact","Iterable","Klaviyo","LinkedInAdsCAPI","LiveIntent","LiveRampWarehouse","MNTN","MNTNAudience","Mailchimp","MicrosoftCAPI","Mixpanel","NextdoorAds","OpenAIAds","OursSyntheticData","Outbrain","Partnerize","Pinterest","Plausible","Podscribe","PostHog","QuantcastCAPI","QuoraAds","Reddit","Rokt","RokuCAPI","SnapchatAdsCapi","Spotify","StackAdaptAPI","Taboola","Tatari","TatariCAPI","TheTradeDesk","TikTok","UniversalAds","VWO","Viant","ViantCAPI","Vibe","VibeAudience","Woopra","XAds","YelpCAPI","Zendesk","ZohoCRM","ZoomInfo"],"description":"Destination type. Read responses may include warehouse, cloud-storage, and audience-sync types that are not creatable through POST /rest/v1/destinations."},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"settings":{"type":"object","nullable":true},"limitedToSourceIds":{"type":"array","nullable":true,"items":{"type":"string"}},"hashingSalt":{"type":"string","nullable":true}},"required":["id","type","status","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteDestinations","tags":["Destinations"],"x-stainless-method":"destinations.delete","security":[{"apiKey":[]}],"description":"Delete a destination. Requires scope: destination:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"boolean"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/destinations/types":{"get":{"operationId":"getDestinationTypes","tags":["Destinations"],"x-stainless-method":"destinations.types","security":[{"apiKey":[]}],"description":"Lists every destination type the platform supports, with its human-readable label, capability flags (oauth, listsAccounts, supportsRenamedEvents), and the settings descriptor used to configure a destination of that type. Account-agnostic — the response is the same for every API key. Filter client-side to find a specific type (e.g. `Klaviyo`, `Facebook`). Requires scope: destination:list","responses":{"200":{"description":"Destination types with settings descriptors and capabilities","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["Audiohook","BasisPostback","Outbrain","OursSyntheticData","FullContact","ZoomInfo","TheTradeDesk","Braze","LiveIntent","ConvertABTestingEvent","Customerio","BingAds","BingAdsWeb","MicrosoftCAPI","HTTPDestination","Woopra","HTTPCustomRequest","Google","GoogleAdsServerContainer","G4Analytics","GA4ServerProxy","GA4MeasurementProtocol","GoogleAds360","Facebook","Mixpanel","Amplitude","TikTok","Reddit","Podscribe","Pinterest","Mailchimp","AWSKinesis","AWSLambda","AWSSNS","GooglePubSub","LinkedInAdsCAPI","ActiveCampaignApi","StackAdaptAPI","Hubspot","Klaviyo","XAds","QuoraAds","SnapchatAdsCapi","Partnerize","NextdoorAds","Tatari","TatariCAPI","Viant","ViantCAPI","Impact","Spotify","Taboola","AmazonDSP","AppLovin","IHeartMediaMagellan","Vibe","GoogleDataManagerEventIngest","Zendesk","Iterable","ArtsAI","QuantcastCAPI","FloodlightSGTM","VWO","Attentive","Admitad","Plausible","PostHog","RokuCAPI","Everflow","BeeswaxPostback","AdobeAnalytics","UniversalAds","OpenAIAds","YelpCAPI","MNTN","ZohoCRM","Rokt","Fullstory"]},"label":{"type":"string"},"status":{"type":"string","enum":["deprecated","ga"]},"capabilities":{"type":"object","properties":{"oauth":{"type":"boolean"},"listsAccounts":{"type":"boolean"},"supportsRenamedEvents":{"type":"boolean"}},"required":["oauth","listsAccounts","supportsRenamedEvents"]},"settings":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string","enum":["Alert"]},"label":{"type":"string","description":"Informational display message only. Do not send this key in POST or PATCH settings."}},"required":["key","type","label"],"additionalProperties":false},{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string","enum":["Select"]},"label":{"type":"string"},"sublabel":{"type":"string","nullable":true},"required":{"type":"boolean","nullable":true},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]}},"defaultValue":{"type":"string","nullable":true}},"required":["key","type","label","options"],"additionalProperties":false},{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string","enum":["Switch"]},"label":{"type":"string"},"required":{"type":"boolean","nullable":true},"sublabel":{"type":"string","nullable":true},"defaultValue":{"type":"boolean","nullable":true}},"required":["key","type","label"],"additionalProperties":false},{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string","enum":["GenericOauth"]},"label":{"type":"string"},"sublabel":{"type":"string","nullable":true}},"required":["key","type","label"],"additionalProperties":false},{"type":"object","properties":{"key":{"type":"string"},"type":{"anyOf":[{"type":"string","enum":["Text"]},{"type":"string","enum":["Secret"]}]},"label":{"type":"string"},"sublabel":{"type":"string","nullable":true},"placeholder":{"type":"string"},"required":{"type":"boolean","nullable":true}},"required":["key","type","label","placeholder"],"additionalProperties":false}]}}},"required":["id","label","status","capabilities","settings"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/destinations/{id}/health":{"get":{"operationId":"healthDestinations","tags":["Destinations"],"x-stainless-method":"destinations.health","security":[{"apiKey":[]}],"description":"Snapshot of dispatch health for this destination over the trailing 24 hours: counts of succeeded, failed, and intentionally stopped/blocked dispatches, plus a derived `status`. Requires scope: destination:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Dispatch health snapshot for the destination","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["DEGRADED","HEALTHY","NO_DATA","UNHEALTHY"],"description":"HEALTHY (>=95% of decisive dispatches succeeded), DEGRADED (50-95%), UNHEALTHY (<50%), or NO_DATA (no succeeded/failed dispatches in the window)."},"windowStart":{"type":"string","description":"Start of the snapshot window (ISO 8601)."},"windowEnd":{"type":"string","description":"End of the snapshot window (ISO 8601)."},"totalDispatches":{"type":"number","description":"All dispatch attempts in the window, including stopped/blocked ones."},"successCount":{"type":"number"},"failureCount":{"type":"number"},"stoppedCount":{"type":"number","description":"Dispatches intentionally stopped before send (not a failure)."},"globalStopCount":{"type":"number","description":"Dispatches blocked by an account-wide dispatch rule (not a failure)."},"successRate":{"type":"number","nullable":true,"description":"successCount / (successCount + failureCount). Null when there were no succeeded/failed dispatches in the window."},"lastDispatchedAt":{"type":"string","nullable":true,"description":"Timestamp of the most recent dispatch attempt to this destination, if any."}},"required":["status","windowStart","windowEnd","totalDispatches","successCount","failureCount","stoppedCount","globalStopCount"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/sources":{"get":{"operationId":"listSources","tags":["Sources"],"x-stainless-method":"sources.list","security":[{"apiKey":[]}],"description":"List all sources for this account. Supports cursor pagination and optional filters for `type`, `status`, and `nameContains`. Results are sorted by creation date descending. Requires scope: source:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["AlchemerWebhook","AndroidNativeApi","Branch","CSharpApi","CalComWebhooks","CalendlyWebhook","CallRail","CallTrackingMetrics","DotNetApi","FacebookLeadAds","FormsortWebhooks","Formstack","GoLangApi","HTTPApiSource","Healthie","Heyflow","HubspotAppActions","HubspotFormWebhook","JotFormWebhooks","KotlinApi","NodejsApi","PHPApi","PixelImage","PythonApi","ReactNativeApi","RedirectSource","RubyApi","SegmentWebPlugin","TypeformWebhooks","WebSource","Webhook","WhatConverts","iOSNativeApi"],"description":"Filter by source type."},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","enum":["Disabled","Enabled"],"description":"Filter by source status."},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Case-insensitive substring filter on the source name."},"required":false,"name":"nameContains","in":"query"}],"responses":{"200":{"description":"Paginated list of sources","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AlchemerWebhook","AndroidNativeApi","Branch","CSharpApi","CalComWebhooks","CalendlyWebhook","CallRail","CallTrackingMetrics","DotNetApi","FacebookLeadAds","FormsortWebhooks","Formstack","GoLangApi","HTTPApiSource","Healthie","Heyflow","HubspotAppActions","HubspotFormWebhook","JotFormWebhooks","KotlinApi","NodejsApi","PHPApi","PixelImage","PythonApi","ReactNativeApi","RedirectSource","RubyApi","SegmentWebPlugin","TypeformWebhooks","WebSource","Webhook","WhatConverts","iOSNativeApi"]},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this source."},"isPublished":{"type":"boolean","description":"Whether this source exists in the currently published version. A source that is not published will not accept events.","nullable":true},"lastTriggeredAt":{"type":"string","description":"ISO-8601 timestamp of the most recent inbound request received by this source. Useful for debugging \"is my webhook even reaching us?\"","nullable":true},"lastDispatchedAt":{"type":"string","description":"ISO-8601 timestamp of the most recent event from this source successfully dispatched to a destination.","nullable":true},"selectedAccountId":{"type":"string","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can send events to the CDP. When set, only requests from these domains are accepted for this source. Separate from experiment settings `whitelistDomains`, which limits which domains can load your experiments."},"whitelistIps":{"type":"array","nullable":true,"items":{"type":"string"}},"botControlMode":{"type":"string","nullable":true},"botScoreThreshold":{"type":"number","nullable":true},"excludeRequestContext":{"type":"boolean","nullable":true},"redirectUrl":{"type":"string","nullable":true},"projectAPIKey":{"type":"string","nullable":true},"probabilisticIdentity":{"type":"object","nullable":true}},"required":["id","type","status","createdAt","accountId"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createSources","tags":["Sources"],"x-stainless-method":"sources.create","security":[{"apiKey":[]}],"description":"Create a new source. Returns the full source entity (same shape as GET /sources/{id}) so callers can read all server-assigned fields without a follow-up GET. Requires scope: source:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AlchemerWebhook","AndroidNativeApi","Branch","CSharpApi","CalComWebhooks","CalendlyWebhook","CallRail","CallTrackingMetrics","DotNetApi","FacebookLeadAds","FormsortWebhooks","Formstack","GoLangApi","HTTPApiSource","Healthie","Heyflow","HubspotAppActions","HubspotFormWebhook","JotFormWebhooks","KotlinApi","NodejsApi","PHPApi","PixelImage","PythonApi","ReactNativeApi","RedirectSource","RubyApi","SegmentWebPlugin","TypeformWebhooks","WebSource","Webhook","WhatConverts","iOSNativeApi"]},"redirectUrl":{"type":"string","description":"Destination URL for a RedirectSource (short link). Ignored by other source types. Must be a valid http(s) URL.","nullable":true}},"required":["type"]}}}},"responses":{"200":{"description":"Created source","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AlchemerWebhook","AndroidNativeApi","Branch","CSharpApi","CalComWebhooks","CalendlyWebhook","CallRail","CallTrackingMetrics","DotNetApi","FacebookLeadAds","FormsortWebhooks","Formstack","GoLangApi","HTTPApiSource","Healthie","Heyflow","HubspotAppActions","HubspotFormWebhook","JotFormWebhooks","KotlinApi","NodejsApi","PHPApi","PixelImage","PythonApi","ReactNativeApi","RedirectSource","RubyApi","SegmentWebPlugin","TypeformWebhooks","WebSource","Webhook","WhatConverts","iOSNativeApi"]},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this source."},"isPublished":{"type":"boolean","description":"Whether this source exists in the currently published version. A source that is not published will not accept events.","nullable":true},"lastTriggeredAt":{"type":"string","description":"ISO-8601 timestamp of the most recent inbound request received by this source. Useful for debugging \"is my webhook even reaching us?\"","nullable":true},"lastDispatchedAt":{"type":"string","description":"ISO-8601 timestamp of the most recent event from this source successfully dispatched to a destination.","nullable":true},"selectedAccountId":{"type":"string","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can send events to the CDP. When set, only requests from these domains are accepted for this source. Separate from experiment settings `whitelistDomains`, which limits which domains can load your experiments."},"whitelistIps":{"type":"array","nullable":true,"items":{"type":"string"}},"botControlMode":{"type":"string","nullable":true},"botScoreThreshold":{"type":"number","nullable":true},"excludeRequestContext":{"type":"boolean","nullable":true},"redirectUrl":{"type":"string","nullable":true},"projectAPIKey":{"type":"string","nullable":true},"probabilisticIdentity":{"type":"object","nullable":true}},"required":["id","type","status","createdAt","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/sources/{id}":{"get":{"operationId":"findSources","tags":["Sources"],"x-stainless-method":"sources.retrieve","security":[{"apiKey":[]}],"description":"Find a single source by ID. Requires scope: source:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Source details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AlchemerWebhook","AndroidNativeApi","Branch","CSharpApi","CalComWebhooks","CalendlyWebhook","CallRail","CallTrackingMetrics","DotNetApi","FacebookLeadAds","FormsortWebhooks","Formstack","GoLangApi","HTTPApiSource","Healthie","Heyflow","HubspotAppActions","HubspotFormWebhook","JotFormWebhooks","KotlinApi","NodejsApi","PHPApi","PixelImage","PythonApi","ReactNativeApi","RedirectSource","RubyApi","SegmentWebPlugin","TypeformWebhooks","WebSource","Webhook","WhatConverts","iOSNativeApi"]},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this source."},"isPublished":{"type":"boolean","description":"Whether this source exists in the currently published version. A source that is not published will not accept events.","nullable":true},"lastTriggeredAt":{"type":"string","description":"ISO-8601 timestamp of the most recent inbound request received by this source. Useful for debugging \"is my webhook even reaching us?\"","nullable":true},"lastDispatchedAt":{"type":"string","description":"ISO-8601 timestamp of the most recent event from this source successfully dispatched to a destination.","nullable":true},"selectedAccountId":{"type":"string","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can send events to the CDP. When set, only requests from these domains are accepted for this source. Separate from experiment settings `whitelistDomains`, which limits which domains can load your experiments."},"whitelistIps":{"type":"array","nullable":true,"items":{"type":"string"}},"botControlMode":{"type":"string","nullable":true},"botScoreThreshold":{"type":"number","nullable":true},"excludeRequestContext":{"type":"boolean","nullable":true},"redirectUrl":{"type":"string","nullable":true},"projectAPIKey":{"type":"string","nullable":true},"probabilisticIdentity":{"type":"object","nullable":true}},"required":["id","type","status","createdAt","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchSources","tags":["Sources"],"x-stainless-method":"sources.update","security":[{"apiKey":[]}],"description":"Partially update a source. Only the fields you send are changed; omitted fields are unchanged. Send explicit `null` to clear a nullable field. Returns the full source entity after the update. Requires scope: source:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","nullable":true},"botControlMode":{"type":"string","nullable":true},"botScoreThreshold":{"type":"number","nullable":true},"excludeRequestContext":{"type":"boolean","nullable":true},"name":{"type":"string","nullable":true},"probabilisticIdentity":{"type":"object","nullable":true},"projectAPIKey":{"type":"string","nullable":true},"redirectUrl":{"type":"string","nullable":true},"selectedAccountId":{"type":"string","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"}},"whitelistIps":{"type":"array","nullable":true,"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Updated source","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"type":{"type":"string","enum":["AlchemerWebhook","AndroidNativeApi","Branch","CSharpApi","CalComWebhooks","CalendlyWebhook","CallRail","CallTrackingMetrics","DotNetApi","FacebookLeadAds","FormsortWebhooks","Formstack","GoLangApi","HTTPApiSource","Healthie","Heyflow","HubspotAppActions","HubspotFormWebhook","JotFormWebhooks","KotlinApi","NodejsApi","PHPApi","PixelImage","PythonApi","ReactNativeApi","RedirectSource","RubyApi","SegmentWebPlugin","TypeformWebhooks","WebSource","Webhook","WhatConverts","iOSNativeApi"]},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this source."},"isPublished":{"type":"boolean","description":"Whether this source exists in the currently published version. A source that is not published will not accept events.","nullable":true},"lastTriggeredAt":{"type":"string","description":"ISO-8601 timestamp of the most recent inbound request received by this source. Useful for debugging \"is my webhook even reaching us?\"","nullable":true},"lastDispatchedAt":{"type":"string","description":"ISO-8601 timestamp of the most recent event from this source successfully dispatched to a destination.","nullable":true},"selectedAccountId":{"type":"string","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can send events to the CDP. When set, only requests from these domains are accepted for this source. Separate from experiment settings `whitelistDomains`, which limits which domains can load your experiments."},"whitelistIps":{"type":"array","nullable":true,"items":{"type":"string"}},"botControlMode":{"type":"string","nullable":true},"botScoreThreshold":{"type":"number","nullable":true},"excludeRequestContext":{"type":"boolean","nullable":true},"redirectUrl":{"type":"string","nullable":true},"projectAPIKey":{"type":"string","nullable":true},"probabilisticIdentity":{"type":"object","nullable":true}},"required":["id","type","status","createdAt","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteSources","tags":["Sources"],"x-stainless-method":"sources.delete","security":[{"apiKey":[]}],"description":"Delete a source. Requires scope: source:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/sources/{id}/tokens":{"get":{"operationId":"getSourceTokens","tags":["Sources"],"x-stainless-method":"sources.tokens","security":[{"apiKey":[]}],"description":"Returns the install or ingest tokens for a source. The response is a discriminated union on `sourceType`: pixel sources return `{ sourceType: \"pixel\", token, testToken, installScript, testInstallScript }`, and webhook sources return `{ sourceType: \"webhook\", token, testToken, ingestUrl, testIngestUrl, sampleCurl }`. Inspect the source's `type` field (`GET /rest/v1/sources/{id}`) to know which variant to expect. Requires scope: source:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Source tokens. The shape depends on the source — pixel/web sources return an `installScript`; webhook sources return an `ingestUrl` and `sampleCurl`. The `sourceType` field discriminates between the two variants.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"sourceType":{"type":"string","enum":["pixel"],"description":"Discriminator: this is a pixel/web source."},"token":{"type":"string","description":"Install token for the source."},"testToken":{"type":"string","description":"Test-mode token derived from `token`."},"installScript":{"type":"string","description":"Ready-to-paste install snippet for the production token, including linked runtime tokens for supported modules."},"testInstallScript":{"type":"string","description":"Ready-to-paste install snippet for the test token, suitable for validation before a live install."}},"required":["sourceType","token","testToken","installScript","testInstallScript"]},{"type":"object","properties":{"sourceType":{"type":"string","enum":["webhook"],"description":"Discriminator: this is a webhook source."},"ingestUrl":{"type":"string","description":"Production ingest URL for the webhook source."},"testIngestUrl":{"type":"string","description":"Test-mode ingest URL."},"token":{"type":"string","description":"Ingest token embedded in the webhook URL path."},"testToken":{"type":"string","description":"Test-mode ingest token derived from `token`."},"sampleCurl":{"type":"string","description":"Example curl command showing how to POST a sample event to the ingest URL. Copy and run to verify connectivity."}},"required":["sourceType","ingestUrl","testIngestUrl","token","testToken","sampleCurl"]}],"description":"Token data for the source. The `sourceType` field discriminates between pixel sources (web / PixelImage) which return an `installScript`, and webhook sources which return an `ingestUrl` and `sampleCurl` instead. Pixel sources use `installScript` / `testInstallScript` to add the tracking pixel to a website. Webhook sources POST JSON to `ingestUrl` directly."}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/allowed-events":{"get":{"operationId":"listAllowedEvents","tags":["Allowed Events"],"x-stainless-method":"allowed_events.list","security":[{"apiKey":[]}],"description":"List every allowed event for this account. Allowed events sit between sources and destinations in the dispatch flow — only inbound events whose `event` field matches the `name` of an allowed event (case-insensitive) can be routed to that event's `destinationIds`. Events without a matching allowed event are dropped. The list is not paginated; the per-account count is bounded. System events (names beginning with `$`, e.g. `$heatmap_click`) are hidden from the response — only `$identify` is creatable as an allowed event. Requires scope: allowedEvent:list","responses":{"200":{"description":"List of allowed events","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this allowed event."},"name":{"type":"string","description":"Case-insensitive event name. Inbound events whose `event` field matches this value are gated through this allowed event. Reserved: names starting with `$` are system events and cannot be created (except `$identify`)."},"destinationIds":{"type":"array","items":{"type":"string"},"description":"Destinations that receive this event. Empty array means the event is allowed but routed nowhere (effectively dropped). PATCH replaces this list wholesale."},"trigger":{"type":"string","description":"Optional free-form trigger description. Not used by the dispatch pipeline — surfaced in the dashboard so teams can record where each event fires.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the allowed event was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last PATCH. Equal to createdAt on a freshly created event.","nullable":true}},"required":["id","name","destinationIds","createdAt"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createAllowedEvents","tags":["Allowed Events"],"x-stainless-method":"allowed_events.create","security":[{"apiKey":[]}],"description":"Create a new allowed event for this account.\n\n- `name` is required, trimmed, case-insensitively unique within the account, and rejected if it exceeds the platform event-name length limit.\n- Names starting with `$` are reserved for system events. Only `$identify` is accepted.\n- `destinationIds` is optional. Unknown ids and ids belonging to other accounts are silently filtered out at write time (the destination must exist on this account to be saved).\n\nReturns the full entity so callers can read the server-assigned `id`, `createdAt`, and the filtered `destinationIds` without a follow-up GET. Known input failures (duplicate name, name length, `$`-prefix reservation, empty name) are returned as HTTP 409 with the reason in the response `error` field. Requires scope: allowedEvent:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"destinationIds":{"type":"array","nullable":true,"items":{"type":"string"}}},"required":["name"]}}}},"responses":{"200":{"description":"Created allowed event","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this allowed event."},"name":{"type":"string","description":"Case-insensitive event name. Inbound events whose `event` field matches this value are gated through this allowed event. Reserved: names starting with `$` are system events and cannot be created (except `$identify`)."},"destinationIds":{"type":"array","items":{"type":"string"},"description":"Destinations that receive this event. Empty array means the event is allowed but routed nowhere (effectively dropped). PATCH replaces this list wholesale."},"trigger":{"type":"string","description":"Optional free-form trigger description. Not used by the dispatch pipeline — surfaced in the dashboard so teams can record where each event fires.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the allowed event was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last PATCH. Equal to createdAt on a freshly created event.","nullable":true},"lastTriggeredAt":{"type":"string","description":"ISO 8601 timestamp of the most recent inbound event observed for this name. Useful for spotting events that were configured but never fired. Null when never observed.","nullable":true},"lastDispatchedAt":{"type":"string","description":"ISO 8601 timestamp of the most recent successful dispatch to any destination on `destinationIds`. Lags `lastTriggeredAt` when consent or governance rules drop the event before dispatch.","nullable":true}},"required":["id","name","destinationIds","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — duplicate event name on this account, name too long, name starts with `$` (other than `$identify`), or empty name. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/allowed-events/{id}":{"get":{"operationId":"findAllowedEvents","tags":["Allowed Events"],"x-stainless-method":"allowed_events.retrieve","security":[{"apiKey":[]}],"description":"Fetch a single allowed event by id. Returns 404 when no record matches the supplied id or it belongs to a different account. Requires scope: allowedEvent:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Allowed event details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this allowed event."},"name":{"type":"string","description":"Case-insensitive event name. Inbound events whose `event` field matches this value are gated through this allowed event. Reserved: names starting with `$` are system events and cannot be created (except `$identify`)."},"destinationIds":{"type":"array","items":{"type":"string"},"description":"Destinations that receive this event. Empty array means the event is allowed but routed nowhere (effectively dropped). PATCH replaces this list wholesale."},"trigger":{"type":"string","description":"Optional free-form trigger description. Not used by the dispatch pipeline — surfaced in the dashboard so teams can record where each event fires.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the allowed event was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last PATCH. Equal to createdAt on a freshly created event.","nullable":true},"lastTriggeredAt":{"type":"string","description":"ISO 8601 timestamp of the most recent inbound event observed for this name. Useful for spotting events that were configured but never fired. Null when never observed.","nullable":true},"lastDispatchedAt":{"type":"string","description":"ISO 8601 timestamp of the most recent successful dispatch to any destination on `destinationIds`. Lags `lastTriggeredAt` when consent or governance rules drop the event before dispatch.","nullable":true}},"required":["id","name","destinationIds","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchAllowedEvents","tags":["Allowed Events"],"x-stainless-method":"allowed_events.update","security":[{"apiKey":[]}],"description":"Partially update an allowed event. Only the fields you send are changed; omitted fields keep their current value.\n\n- `destinationIds` is replaced wholesale when sent — the canonical way to add or remove a destination is to fetch, modify the array, and PATCH it back. Stale ids (deleted destinations or destinations on another account) are silently filtered out at write time.\n- `name` is subject to the same rules as create: case-insensitive uniqueness, length cap, `$`-prefix reservation.\n- `trigger` accepts `null` to clear the existing value.\n\nReturns the full entity. Known input failures (duplicate name, length, `$`-prefix, empty name) are returned as HTTP 409 with the reason in the response `error` field. Requires scope: allowedEvent:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New event name. Subject to the same rules as create: case-insensitive uniqueness within the account, max length enforced by the platform, and the `$`-prefix reservation (only `$identify` is allowed). Omit to leave the name unchanged.","nullable":true},"destinationIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Destinations that should receive this event. Wholesale replacement — the sent list becomes the new value. Stale IDs (destinations from another account or deleted destinations) are silently filtered out at write time. Send `[]` to gate the event from every destination."},"trigger":{"type":"string","description":"Free-form trigger description shown in the dashboard. Send `null` to clear. Not used by the dispatch pipeline.","nullable":true}}}}}},"responses":{"200":{"description":"Updated allowed event","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this allowed event."},"name":{"type":"string","description":"Case-insensitive event name. Inbound events whose `event` field matches this value are gated through this allowed event. Reserved: names starting with `$` are system events and cannot be created (except `$identify`)."},"destinationIds":{"type":"array","items":{"type":"string"},"description":"Destinations that receive this event. Empty array means the event is allowed but routed nowhere (effectively dropped). PATCH replaces this list wholesale."},"trigger":{"type":"string","description":"Optional free-form trigger description. Not used by the dispatch pipeline — surfaced in the dashboard so teams can record where each event fires.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the allowed event was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last PATCH. Equal to createdAt on a freshly created event.","nullable":true},"lastTriggeredAt":{"type":"string","description":"ISO 8601 timestamp of the most recent inbound event observed for this name. Useful for spotting events that were configured but never fired. Null when never observed.","nullable":true},"lastDispatchedAt":{"type":"string","description":"ISO 8601 timestamp of the most recent successful dispatch to any destination on `destinationIds`. Lags `lastTriggeredAt` when consent or governance rules drop the event before dispatch.","nullable":true}},"required":["id","name","destinationIds","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — name change collides with an existing allowed event on this account, name too long, name starts with `$` (other than `$identify`), or empty name. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteAllowedEvents","tags":["Allowed Events"],"x-stainless-method":"allowed_events.delete","security":[{"apiKey":[]}],"description":"Delete an allowed event. After deletion, inbound events whose `event` field matches the deleted name are no longer routed and are dropped at the allow-list stage of the dispatch flow. Requires scope: allowedEvent:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"boolean"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/consent-settings":{"get":{"operationId":"listConsentSettings","tags":["Consent Settings"],"x-stainless-method":"consent_settings.list","security":[{"apiKey":[]}],"description":"List all consent settings. Requires scope: consentSettings:list","responses":{"200":{"description":"List of consent settings","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this consent settings record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard."},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"consentSettings\"."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Enabled means the CMP serves on whitelisted domains; Disabled means it does not."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Null on a freshly created record.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Allowlist of domains where this CMP configuration may run. Used at runtime to derive the broadest matching base domain so consent can persist across matching subdomains."},"consentCookieName":{"type":"string","description":"Name of the cookie that stores the user's consent state. Defaults to \"op_consent\".","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\") so consent and SDK events resolve to the same visitor.","nullable":true},"webSDKToken":{"type":"string","description":"Pixel of the WebSource that this CMP is wired into. Setting this to a token that is not a valid WebSource of yours is rejected; use null to clear the link.","nullable":true},"customDomain":{"type":"string","description":"Optional custom CDN domain for serving the CMP script (e.g. consent.example.com).","nullable":true},"revision":{"type":"number","description":"Revision counter. Bump this to force users who already consented to see the modal again (the SDK compares the persisted revision against this value).","nullable":true},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"CSS class names that opt scripts out of consent blocking. Each entry must be a single class token (no whitespace)."},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Per-service entries powering \"show vendors\" and category-aware blocking."},"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Top-level consent categories (e.g. necessary / analytics / advertising). Server re-stamps `priority` to 0..N on write."},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Default rule used when the user is not in any region listed in `regions[]`."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Per-region rule overrides. The first rule whose `regionCode`/`additionalRegions` includes the user's region wins; otherwise `default` applies."}},"required":["id","name","kind","status","createdAt","services","categories","default","regions"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createConsentSettings","tags":["Consent Settings"],"x-stainless-method":"consent_settings.create","security":[{"apiKey":[]}],"description":"Create a new consent settings record. POST takes no request body — the server initializes the record with defaults (Disabled status, opt-out default rule, English translations, necessary/analytics/advertising categories, no regions, no whitelisted domains). Configure the record afterward with PATCH (partial update) or PUT (full replacement). Returns the same shape as GET so you can read the server-assigned `id`, default rule, and categories without a follow-up fetch. Requires scope: consentSettings:create","responses":{"200":{"description":"Created consent setting","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this consent settings record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard."},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"consentSettings\"."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Enabled means the CMP serves on whitelisted domains; Disabled means it does not."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Null on a freshly created record.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Allowlist of domains where this CMP configuration may run. Used at runtime to derive the broadest matching base domain so consent can persist across matching subdomains."},"consentCookieName":{"type":"string","description":"Name of the cookie that stores the user's consent state. Defaults to \"op_consent\".","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\") so consent and SDK events resolve to the same visitor.","nullable":true},"webSDKToken":{"type":"string","description":"Pixel of the WebSource that this CMP is wired into. Setting this to a token that is not a valid WebSource of yours is rejected; use null to clear the link.","nullable":true},"customDomain":{"type":"string","description":"Optional custom CDN domain for serving the CMP script (e.g. consent.example.com).","nullable":true},"revision":{"type":"number","description":"Revision counter. Bump this to force users who already consented to see the modal again (the SDK compares the persisted revision against this value).","nullable":true},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"CSS class names that opt scripts out of consent blocking. Each entry must be a single class token (no whitespace)."},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Per-service entries powering \"show vendors\" and category-aware blocking."},"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Top-level consent categories (e.g. necessary / analytics / advertising). Server re-stamps `priority` to 0..N on write."},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Default rule used when the user is not in any region listed in `regions[]`."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Per-region rule overrides. The first rule whose `regionCode`/`additionalRegions` includes the user's region wins; otherwise `default` applies."}},"required":["id","name","kind","status","createdAt","services","categories","default","regions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/consent-settings/{id}":{"get":{"operationId":"findConsentSettings","tags":["Consent Settings"],"x-stainless-method":"consent_settings.retrieve","security":[{"apiKey":[]}],"description":"Find a single consent setting by ID. Requires scope: consentSettings:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Consent Settings details","content":{"application/json":{"schema":{"type":"object","nullable":true,"properties":{"id":{"type":"string","description":"Server-assigned UUID for this consent settings record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard."},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"consentSettings\"."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Enabled means the CMP serves on whitelisted domains; Disabled means it does not."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Null on a freshly created record.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Allowlist of domains where this CMP configuration may run. Used at runtime to derive the broadest matching base domain so consent can persist across matching subdomains."},"consentCookieName":{"type":"string","description":"Name of the cookie that stores the user's consent state. Defaults to \"op_consent\".","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\") so consent and SDK events resolve to the same visitor.","nullable":true},"webSDKToken":{"type":"string","description":"Pixel of the WebSource that this CMP is wired into. Setting this to a token that is not a valid WebSource of yours is rejected; use null to clear the link.","nullable":true},"customDomain":{"type":"string","description":"Optional custom CDN domain for serving the CMP script (e.g. consent.example.com).","nullable":true},"revision":{"type":"number","description":"Revision counter. Bump this to force users who already consented to see the modal again (the SDK compares the persisted revision against this value).","nullable":true},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"CSS class names that opt scripts out of consent blocking. Each entry must be a single class token (no whitespace)."},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Per-service entries powering \"show vendors\" and category-aware blocking."},"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Top-level consent categories (e.g. necessary / analytics / advertising). Server re-stamps `priority` to 0..N on write."},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Default rule used when the user is not in any region listed in `regions[]`."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Per-region rule overrides. The first rule whose `regionCode`/`additionalRegions` includes the user's region wins; otherwise `default` applies."}},"required":["id","name","kind","status","createdAt","services","categories","default","regions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchConsentSettings","tags":["Consent Settings"],"x-stainless-method":"consent_settings.update","security":[{"apiKey":[]}],"description":"Partially update a consent setting. Send only the fields you want to change — every field is optional and unspecified fields are preserved. List-valued fields (services, categories, regions) are replaced wholesale when sent. Requires scope: consentSettings:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Rename the consent settings record."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Toggle Enabled/Disabled without re-sending the rest of the config."},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Replace the allowlist. Pass null/[] to clear."},"consentCookieName":{"type":"string","description":"Set or clear the consent cookie name.","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\"). Pass null to clear.","nullable":true},"webSDKToken":{"type":"string","description":"Set or clear the WebSource pixel link. A non-null token must be a valid WebSource of yours.","nullable":true},"customDomain":{"type":"string","description":"Set or clear the custom CDN domain.","nullable":true},"revision":{"type":"number","description":"Bump the revision counter to re-prompt users.","nullable":true},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Replace the entire services list. Omit to leave existing services untouched."},"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Replace the entire categories list. Omit to leave existing categories untouched."},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Replace the default rule wholesale. Omit to leave it untouched."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Replace the entire regions list. Omit to leave it untouched. To change one region, send the full regions array with that region's rule modified."},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Replace the skipBlockingClassNames list. Pass null/[] to clear."}}}}}},"responses":{"200":{"description":"Updated consent setting","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this consent settings record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard."},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"consentSettings\"."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Enabled means the CMP serves on whitelisted domains; Disabled means it does not."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Null on a freshly created record.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Allowlist of domains where this CMP configuration may run. Used at runtime to derive the broadest matching base domain so consent can persist across matching subdomains."},"consentCookieName":{"type":"string","description":"Name of the cookie that stores the user's consent state. Defaults to \"op_consent\".","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\") so consent and SDK events resolve to the same visitor.","nullable":true},"webSDKToken":{"type":"string","description":"Pixel of the WebSource that this CMP is wired into. Setting this to a token that is not a valid WebSource of yours is rejected; use null to clear the link.","nullable":true},"customDomain":{"type":"string","description":"Optional custom CDN domain for serving the CMP script (e.g. consent.example.com).","nullable":true},"revision":{"type":"number","description":"Revision counter. Bump this to force users who already consented to see the modal again (the SDK compares the persisted revision against this value).","nullable":true},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"CSS class names that opt scripts out of consent blocking. Each entry must be a single class token (no whitespace)."},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Per-service entries powering \"show vendors\" and category-aware blocking."},"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Top-level consent categories (e.g. necessary / analytics / advertising). Server re-stamps `priority` to 0..N on write."},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Default rule used when the user is not in any region listed in `regions[]`."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Per-region rule overrides. The first rule whose `regionCode`/`additionalRegions` includes the user's region wins; otherwise `default` applies."}},"required":["id","name","kind","status","createdAt","services","categories","default","regions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"put":{"operationId":"putConsentSettings","tags":["Consent Settings"],"x-stainless-method":"consent_settings.replace","security":[{"apiKey":[]}],"description":"Replace a consent setting. Send the full ConsentSettingsInput body — omitted optional fields are reset. Use PATCH for partial updates. Requires scope: consentSettings:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Top-level consent categories. Server re-stamps `priority` to 0..N."},"consentCookieName":{"type":"string","description":"Name of the cookie that stores consent state. Pass null to clear (defaults to \"op_consent\").","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\"). Pass null to clear.","nullable":true},"customDomain":{"type":"string","description":"Custom CDN domain for serving the CMP script. Pass null to clear.","nullable":true},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Default rule used when the user is not in any region listed in `regions[]`."},"name":{"type":"string","description":"Human-readable name shown in the dashboard."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Per-region rule overrides. Each `regionCode` must be unique across rules and must not appear in any other rule's `additionalRegions`."},"revision":{"type":"number","description":"Revision counter. Bump to re-prompt users who already consented.","nullable":true},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Per-service entries powering \"show vendors\" and category-aware blocking. Empty array clears the list."},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"CSS class names that opt scripts out of consent blocking. Each must be a single class token."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Enabled to serve the CMP, Disabled to take it offline."},"webSDKToken":{"type":"string","description":"Pixel of the WebSource this CMP is wired into. Pass null to clear the link.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Allowlist of domains where this CMP runs. Pass null/[] to clear."}},"required":["categories","default","name","regions","services","status"]}}}},"responses":{"200":{"description":"Replaced consent setting","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this consent settings record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard."},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"consentSettings\"."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Enabled means the CMP serves on whitelisted domains; Disabled means it does not."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Null on a freshly created record.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Allowlist of domains where this CMP configuration may run. Used at runtime to derive the broadest matching base domain so consent can persist across matching subdomains."},"consentCookieName":{"type":"string","description":"Name of the cookie that stores the user's consent state. Defaults to \"op_consent\".","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\") so consent and SDK events resolve to the same visitor.","nullable":true},"webSDKToken":{"type":"string","description":"Pixel of the WebSource that this CMP is wired into. Setting this to a token that is not a valid WebSource of yours is rejected; use null to clear the link.","nullable":true},"customDomain":{"type":"string","description":"Optional custom CDN domain for serving the CMP script (e.g. consent.example.com).","nullable":true},"revision":{"type":"number","description":"Revision counter. Bump this to force users who already consented to see the modal again (the SDK compares the persisted revision against this value).","nullable":true},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"CSS class names that opt scripts out of consent blocking. Each entry must be a single class token (no whitespace)."},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Per-service entries powering \"show vendors\" and category-aware blocking."},"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Top-level consent categories (e.g. necessary / analytics / advertising). Server re-stamps `priority` to 0..N on write."},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Default rule used when the user is not in any region listed in `regions[]`."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Per-region rule overrides. The first rule whose `regionCode`/`additionalRegions` includes the user's region wins; otherwise `default` applies."}},"required":["id","name","kind","status","createdAt","services","categories","default","regions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteConsentSettings","tags":["Consent Settings"],"x-stainless-method":"consent_settings.delete","security":[{"apiKey":[]}],"description":"Delete a consent setting. Requires scope: consentSettings:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this consent settings record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard."},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"consentSettings\"."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Enabled means the CMP serves on whitelisted domains; Disabled means it does not."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Null on a freshly created record.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Allowlist of domains where this CMP configuration may run. Used at runtime to derive the broadest matching base domain so consent can persist across matching subdomains."},"consentCookieName":{"type":"string","description":"Name of the cookie that stores the user's consent state. Defaults to \"op_consent\".","nullable":true},"deviceIdCookieName":{"type":"string","description":"Cookie/localStorage key for the visitor (device) ID. Must exactly match the web SDK's cookie_names.device_id (default \"ours_device_id\") so consent and SDK events resolve to the same visitor.","nullable":true},"webSDKToken":{"type":"string","description":"Pixel of the WebSource that this CMP is wired into. Setting this to a token that is not a valid WebSource of yours is rejected; use null to clear the link.","nullable":true},"customDomain":{"type":"string","description":"Optional custom CDN domain for serving the CMP script (e.g. consent.example.com).","nullable":true},"revision":{"type":"number","description":"Revision counter. Bump this to force users who already consented to see the modal again (the SDK compares the persisted revision against this value).","nullable":true},"skipBlockingClassNames":{"type":"array","nullable":true,"items":{"type":"string"},"description":"CSS class names that opt scripts out of consent blocking. Each entry must be a single class token (no whitespace)."},"services":{"type":"array","items":{"type":"object","properties":{"additionalCategories":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Extra category values this service belongs to. Each must match a `categories[].value`."},"category":{"type":"string","description":"Primary category value this service belongs to. Must match one of the top-level `categories[].value` entries.","nullable":true},"domainPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Domains/paths this service matches. Patterns matching the CMP's own scripts (e.g. cdn.oursprivacy.com/cmp-init) are rejected to prevent the CMP blocking itself — use a more specific path like cdn.oursprivacy.com/main.js to block a specific script."},"internalNotes":{"type":"string","description":"Internal notes shown to admins in the dashboard. Not user-facing."},"label":{"type":"string","description":"Display name for this service in the preferences modal."}},"required":["internalNotes","label"]},"description":"Per-service entries powering \"show vendors\" and category-aware blocking."},"categories":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label shown next to the toggle in the preferences modal."},"priority":{"type":"integer","description":"Sort key. Lower numbers render first. Server re-stamps to 0..N on write — send any integer, gaps and duplicates are ironed out."},"value":{"type":"string","description":"Stable identifier referenced by services and translation sections. Conventionally lowercase (e.g. \"necessary\", \"analytics\", \"advertising\")."}},"required":["label","priority","value"]},"description":"Top-level consent categories (e.g. necessary / analytics / advertising). Server re-stamps `priority` to 0..N on write."},"default":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"],"description":"Default rule used when the user is not in any region listed in `regions[]`."},"regions":{"type":"array","items":{"type":"object","properties":{"additionalRegions":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Other region codes that should reuse this rule. Same code-format rules as `regionCode`. Cannot include `regionCode` itself, cannot duplicate, cannot overlap with another rule's regions."},"name":{"type":"string","description":"Optional label for this override group, e.g. \"CCPA States\". Shown in the dashboard instead of the region codes. Pass null to clear it and fall back to the codes.","nullable":true},"regionCode":{"type":"string","description":"Region this rule applies to. Use ISO 3166-1 alpha-2 country code (\"US\", \"DE\", \"BR\") or country-subdivision code (\"US-CA\", \"GB-ENG\", \"CA-ON\"). Each region code may appear in only one rule across `regions[]`.","example":"US-CA"},"rule":{"type":"object","properties":{"autoShow":{"type":"boolean","description":"When true, the consent modal auto-opens on page load.","nullable":true},"autoShowDismissConfig":{"type":"object","description":"Threshold config for autoShowDismissMode (page count or seconds).","nullable":true},"autoShowDismissMode":{"type":"string","description":"How the modal is treated as dismissed (never, after_pages, after_seconds).","nullable":true},"autoblockUnknown":{"type":"boolean","description":"When true, scripts not classified by services[] are blocked until the user opts in.","nullable":true},"categories":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Category value (matches `categories[].value`) this entry configures."},"value":{"type":"object","properties":{"autoDisableOnGPC":{"type":"boolean","description":"When true, this category defaults off if the browser sends Sec-GPC: 1.","nullable":true},"enabled":{"type":"boolean","description":"Whether this category is on by default before the user interacts."},"readOnly":{"type":"boolean","description":"When true, the user cannot toggle this category in the preferences modal.","nullable":true},"reloadPage":{"type":"boolean","description":"When true, the page reloads after this category is toggled so newly-allowed scripts can run.","nullable":true}},"required":["enabled"]}},"required":["key","value"]},"description":"Per-category default config for this rule. Every category defined in the top-level `categories[].value` should have an entry here."},"disablePageInteraction":{"type":"boolean","description":"When true, the rest of the page is locked behind a backdrop until the user chooses.","nullable":true},"guiOptions":{"type":"object","description":"Visual options for the modals (layout/position/colors).","nullable":true},"hideFromBots":{"type":"boolean","description":"When true, the modal is suppressed for known bot user agents.","nullable":true},"language":{"type":"string","description":"BCP 47 default language for this rule. Must have a matching entry in `translations`. Examples: \"en\", \"en-US\", \"es\", \"de\".","example":"en"},"mode":{"type":"string","enum":["opt_in","opt_out"],"description":"opt_in: scripts blocked until user accepts (GDPR style). opt_out: scripts run by default until user rejects (CCPA style)."},"showVendorsInPreferences":{"type":"boolean","description":"When true, the per-service list (services[]) is rendered inside the preferences modal.","nullable":true},"translations":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string","description":"BCP 47 language tag identifying which translation this entry provides. Examples: \"en\", \"en-US\", \"es\", \"fr-CA\". The default rule's `language` must appear here.","example":"en"},"value":{"type":"object","properties":{"consentModal":{"type":"object","description":"Translated copy for the initial consent modal.","nullable":true},"preferencesModal":{"type":"object","description":"Translated copy for the preferences modal.","nullable":true}}}},"required":["language","value"]},"description":"All UI copy, keyed by language. Must include an entry whose `language` matches the rule's `language` field."}},"required":["categories","language","mode","translations"]}},"required":["regionCode","rule"]},"description":"Per-region rule overrides. The first rule whose `regionCode`/`additionalRegions` includes the user's region wins; otherwise `default` applies."}},"required":["id","name","kind","status","createdAt","services","categories","default","regions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/consent-settings/{id}/analytics":{"get":{"operationId":"getConsentSettingAnalytics","tags":["Consent Settings"],"x-stainless-method":"consent_settings.analytics","security":[{"apiKey":[]}],"description":"Time-series consent analytics for a single consent settings record: banner views, opt-ins, opt-outs, close-icon clicks, and derived opt-in/out rates per UTC day (or per UTC hour with `granularity=HOURLY`). The window is zero-filled so callers get a contiguous series, and rates are person-level (`COUNT(DISTINCT visitor_id)`). Use the optional `pagePath` and `region` filters to scope to one page or one visitor region; use `compareWithPreviousPeriod=true` to also receive the matching prior window. `DAILY` allows a 90-day window; `HOURLY` is capped at 14 days. Requires the API-key scope `report:consent-analytics` (this endpoint returns consent analytics report data, which is PHI-bearing and gated separately from consent-settings management). Requires scope: report:consent-analytics","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 90 days or fewer (14 for `HOURLY` granularity).","example":"2026-04-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 90 days or fewer (14 for `HOURLY` granularity).","example":"2026-04-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","enum":["DAILY","HOURLY"],"description":"Bucket size for the time-series rollup. `DAILY` (default) buckets per UTC day; `HOURLY` buckets per UTC hour and limits the window to 14 days."},"required":false,"name":"granularity","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":2048,"description":"Filter to events whose `default_properties.pathname` equals this value (exact match, case-sensitive). Use this to drill into a single page.","example":"/pricing"},"required":false,"name":"pagePath","in":"query"},{"schema":{"type":"string","description":"Filter results to events whose `request_context.country_region_name` is in this set. Pass a single region (e.g. `California`) or a comma-separated list (`California,Texas`). Case-sensitive. Use `GET /rest/v1/consent-settings/{id}/analytics-by-region` to discover the region names available for an account.","example":"California"},"required":false,"name":"regions","in":"query"},{"schema":{"type":"boolean","description":"When `true`, each bucket also returns the matching bucket from the immediately preceding window of equal length (in `previous*` and `percentageChange*` fields). Defaults to `false`."},"required":false,"name":"compareWithPreviousPeriod","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Optional custom comparison-window lower bound, as a UTC calendar day in `YYYY-MM-DD` format. Provide together with `comparisonTo`; the window between them must be the same length as `from`–`to` (a mismatch returns `400`). When omitted (with `compareWithPreviousPeriod=true`), the immediately preceding equal-length period is used.","example":"2026-03-01"},"required":false,"name":"comparisonFrom","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Optional custom comparison-window upper bound, as a UTC calendar day in `YYYY-MM-DD` format. Provide together with `comparisonFrom`; the window must match the `from`–`to` length. Ignored unless both comparison bounds are supplied.","example":"2026-03-14"},"required":false,"name":"comparisonTo","in":"query"}],"responses":{"200":{"description":"Time-series consent analytics for one consent setting","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"dateTime":{"type":"string"},"bannerViews":{"type":"integer"},"explicitOptIns":{"type":"integer"},"explicitOptOuts":{"type":"integer"},"closeIconClicks":{"type":"integer"},"optInRate":{"type":"number"},"optOutRate":{"type":"number"},"previousBannerViews":{"type":"number","nullable":true},"previousExplicitOptIns":{"type":"number","nullable":true},"previousExplicitOptOuts":{"type":"number","nullable":true},"previousCloseIconClicks":{"type":"number","nullable":true},"previousOptInRate":{"type":"number","nullable":true},"previousOptOutRate":{"type":"number","nullable":true},"percentageChangeBannerViews":{"type":"number","nullable":true},"percentageChangeExplicitOptIns":{"type":"number","nullable":true},"percentageChangeExplicitOptOuts":{"type":"number","nullable":true},"percentageChangeCloseIconClicks":{"type":"number","nullable":true}},"required":["dateTime","bannerViews","explicitOptIns","explicitOptOuts","closeIconClicks","optInRate","optOutRate"]},"description":"One entry per time bucket (day or hour, depending on `granularity`) covering the full window — empty windows are zero-filled so callers can render contiguous time series without gap-handling logic."}},"required":["items"]}}}},"400":{"description":"Bad request — malformed `from`/`to`, window longer than the granularity allows, or filter values out of bounds.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found — no consent setting exists with this `id`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/consent-settings/{id}/page-analysis":{"get":{"operationId":"getConsentSettingPageAnalysis","tags":["Consent Settings"],"x-stainless-method":"consent_settings.pageAnalysis","security":[{"apiKey":[]}],"description":"Per-page consent breakdown for one consent settings record, ranked by opt-outs (descending). Each row bundles banner views, opt-outs, close-icon clicks, and the derived opt-out rate. Documented exception to the cursor-pagination standard: this endpoint paginates with `limit` and `offset` rather than `cursor`. `search` is a substring match against `pathname`; `region` filters to one visitor region. Requires the API-key scope `report:consent-page-analysis` (PHI-bearing report data). Requires scope: report:consent-page-analysis","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 90 days or fewer (14 for `HOURLY` granularity).","example":"2026-04-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 90 days or fewer (14 for `HOURLY` granularity).","example":"2026-04-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":2048,"description":"Case-sensitive substring match against `default_properties.pathname`. Wrapped in `%...%` server-side.","example":"/checkout"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","description":"Filter results to events whose `request_context.country_region_name` is in this set. Pass a single region (e.g. `California`) or a comma-separated list (`California,Texas`). Case-sensitive. Use `GET /rest/v1/consent-settings/{id}/analytics-by-region` to discover the region names available for an account.","example":"California"},"required":false,"name":"regions","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500,"description":"Maximum number of pages to return. Defaults to 50."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Skip this many top-ranked pages before returning. Use together with `limit` for load-more pagination."},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Per-page consent breakdown for one consent setting","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"page":{"type":"string"},"bannerViews":{"type":"integer"},"optOuts":{"type":"integer"},"closeIconClicks":{"type":"integer"},"optOutRate":{"type":"number"}},"required":["page","bannerViews","optOuts","closeIconClicks","optOutRate"]},"description":"Pages with consent activity in the window, ranked by opt-outs (descending). Each page bundles banner views, opt-outs, close-icon clicks, and the derived opt-out rate."},"total":{"type":"integer","description":"Running count of pages loaded so far (`offset + items.length`). Approximate for load-more flows; query without `offset` to get the exact size of the first page."},"hasMore":{"type":"boolean","description":"True when at least one more page is available beyond the current window."}},"required":["items","total","hasMore"]}}}},"400":{"description":"Bad request — malformed `from`/`to`, window longer than 90 days, or `limit`/`offset` out of range.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found — no consent setting exists with this `id`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/consent-settings/{id}/analytics-by-region":{"get":{"operationId":"getConsentSettingAnalyticsByRegion","tags":["Consent Settings"],"x-stainless-method":"consent_settings.analyticsByRegion","security":[{"apiKey":[]}],"description":"Region-grouped consent totals for the window: banner views, opt-ins, opt-outs, close-icon clicks, and derived rates per visitor `country_region_name`, ranked by banner views (descending). Visitors whose region cannot be resolved (e.g. bot traffic, IP geo failure) are bucketed under the literal `Unknown` so per-region counts always sum to the global totals. Use this to discover the region names you can later pass to the `region` filter on `GET /rest/v1/consent-settings/{id}/analytics`. Requires the API-key scope `report:consent-analytics` (PHI-bearing report data). Requires scope: report:consent-analytics","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 90 days or fewer (14 for `HOURLY` granularity).","example":"2026-04-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 90 days or fewer (14 for `HOURLY` granularity).","example":"2026-04-30"},"required":true,"name":"to","in":"query"}],"responses":{"200":{"description":"Region-grouped consent totals for one consent setting","content":{"application/json":{"schema":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"object","properties":{"regionName":{"type":"string"},"bannerViews":{"type":"integer"},"explicitOptIns":{"type":"integer"},"explicitOptOuts":{"type":"integer"},"closeIconClicks":{"type":"integer"},"optInRate":{"type":"number"},"optOutRate":{"type":"number"}},"required":["regionName","bannerViews","explicitOptIns","explicitOptOuts","closeIconClicks","optInRate","optOutRate"]},"description":"Per-region totals for the window, ranked by banner views (descending). Visitors whose region cannot be resolved (e.g. bot traffic, IP geo failure) are bucketed under the literal `Unknown`, so the per-region counts always sum to the global totals."}},"required":["regions"]}}}},"400":{"description":"Bad request — malformed `from`/`to` or window longer than 90 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found — no consent setting exists with this `id`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/consent-analytics":{"get":{"operationId":"listConsentAnalytics","tags":["Consent Analytics"],"x-stainless-method":"consent_analytics.list","security":[{"apiKey":[]}],"description":"Account-wide blocking stats from the Global Consent Center for the window: how many dispatches were attempted, how many were blocked, and a per-category breakdown of the blocks (with `percentageBlocked` = share of `totalDispatches`). Not scoped to a single consent settings record — this aggregates across every destination in the account. The endpoint is identified by query params rather than a path id because the report is account-scoped; this is a documented derived-read exception. Requires the API-key scope `report:global-consent-center-analytics` (this is the account-wide consent analytics report and is gated separately from consent-settings list). Requires scope: report:global-consent-center-analytics","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-04-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-04-30"},"required":true,"name":"to","in":"query"}],"responses":{"200":{"description":"Account-wide blocking stats from the Global Consent Center","content":{"application/json":{"schema":{"type":"object","properties":{"totalDispatches":{"type":"integer","description":"Total dispatches attempted across all destinations for the window."},"totalBlocked":{"type":"integer","description":"Total dispatches blocked by the Global Consent Center across all categories."},"items":{"type":"array","items":{"type":"object","properties":{"categoryName":{"type":"string"},"blockedCount":{"type":"integer"},"percentageBlocked":{"type":"number"}},"required":["categoryName","blockedCount","percentageBlocked"]},"description":"Per-category breakdown of blocked dispatches. `categoryName` is `Unknown` when the upstream block message could not be parsed; `percentageBlocked` is the share of `totalDispatches` blocked under that category."}},"required":["totalDispatches","totalBlocked","items"]}}}},"400":{"description":"Bad request — malformed `from`/`to` or window longer than 60 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/data-governance":{"get":{"operationId":"listDataGovernance","tags":["Data Governance"],"x-stainless-method":"data_governance.list","security":[{"apiKey":[]}],"description":"List the data-governance record(s) on this account. Each account has at most one record, so this list returns either an empty array or a single entity. Cursor pagination is exposed for consistency with other list endpoints but is rarely meaningful here. Data governance is the second stage of the dispatch flow (Source → Allowed Events → Data Governance → Mappings → Destination) — it evaluates each event against the configured category logic and stops dispatch to the destinations on any matching category. Requires scope: globalDispatch:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"List of data-governance records","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this data-governance record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard.","nullable":true},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"dataGovernance\" on the REST surface. The underlying storage discriminator is \"globalDispatchCenter\" — REST translates it on the way out."},"isEnabled":{"type":"boolean","description":"When false, data governance is configured but does not gate dispatch — inbound events flow through to destinations regardless of category logic."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Equal to createdAt on a freshly created record; advances on every PATCH.","nullable":true},"notes":{"type":"string","description":"Free-form notes for this record.","nullable":true},"categories":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the category."},"description":{"type":"string","description":"Optional human-readable description.","nullable":true},"priority":{"type":"integer","description":"1-indexed sort position. Always equals (sorted index + 1) — see PATCH for details."},"destinationIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Destinations gated by this category when its logic evaluates to TRUE."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Condition tree evaluated against each inbound event. Write conditions that evaluate **TRUE for events you want to STOP**. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`); combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nDiscovery: `GET /rest/v1/mappings/default-variables` lists the canonical platform-provided `property` paths (visitor consent arrays, event fields, request context, identity fields). Custom `event.event_properties.*` paths are caller-defined.\n\nExample leaf (stop dispatch when the visitor rejected the `advertising` consent category): `{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }, { \"OR\": [/* nested logic nodes */] }] }`."}},"required":["name","priority"]},"description":"Governance categories in priority order (1..N)."}},"required":["id","kind","isEnabled","createdAt"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createDataGovernance","tags":["Data Governance"],"x-stainless-method":"data_governance.create","security":[{"apiKey":[]}],"description":"Create the data-governance record for this account. Each account may have at most one — a second POST returns 409. Body is optional; defaults are `isEnabled: false` and no categories. Categories are added later via PATCH. Requires scope: globalDispatch:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the new record. Defaults to \"Data Governance\".","nullable":true},"notes":{"type":"string","description":"Free-form notes shown in the dashboard. Not used for routing.","nullable":true},"isEnabled":{"type":"boolean","description":"Whether the record starts enabled. Defaults to false — opt in by setting true here or via PATCH later. When disabled, every category is bypassed and inbound events flow through to destinations regardless of consent state.","nullable":true}}}}}},"responses":{"200":{"description":"Created data-governance record","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this data-governance record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard.","nullable":true},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"dataGovernance\" on the REST surface. The underlying storage discriminator is \"globalDispatchCenter\" — REST translates it on the way out."},"isEnabled":{"type":"boolean","description":"When false, data governance is configured but does not gate dispatch — inbound events flow through to destinations regardless of category logic."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Equal to createdAt on a freshly created record; advances on every PATCH.","nullable":true},"notes":{"type":"string","description":"Free-form notes for this record.","nullable":true},"categories":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the category."},"description":{"type":"string","description":"Optional human-readable description.","nullable":true},"priority":{"type":"integer","description":"1-indexed sort position. Always equals (sorted index + 1) — see PATCH for details."},"destinationIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Destinations gated by this category when its logic evaluates to TRUE."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Condition tree evaluated against each inbound event. Write conditions that evaluate **TRUE for events you want to STOP**. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`); combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nDiscovery: `GET /rest/v1/mappings/default-variables` lists the canonical platform-provided `property` paths (visitor consent arrays, event fields, request context, identity fields). Custom `event.event_properties.*` paths are caller-defined.\n\nExample leaf (stop dispatch when the visitor rejected the `advertising` consent category): `{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }, { \"OR\": [/* nested logic nodes */] }] }`."}},"required":["name","priority"]},"description":"Governance categories in priority order (1..N)."}},"required":["id","kind","isEnabled","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — an account may have at most one data-governance record, and one already exists. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/data-governance/{id}":{"get":{"operationId":"findDataGovernance","tags":["Data Governance"],"x-stainless-method":"data_governance.retrieve","security":[{"apiKey":[]}],"description":"Fetch the data-governance record by id, including its categories (logic, destinations, priority). Returns 404 when no record matches. Requires scope: globalDispatch:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Data governance record details","content":{"application/json":{"schema":{"type":"object","nullable":true,"properties":{"id":{"type":"string","description":"Server-assigned UUID for this data-governance record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard.","nullable":true},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"dataGovernance\" on the REST surface. The underlying storage discriminator is \"globalDispatchCenter\" — REST translates it on the way out."},"isEnabled":{"type":"boolean","description":"When false, data governance is configured but does not gate dispatch — inbound events flow through to destinations regardless of category logic."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Equal to createdAt on a freshly created record; advances on every PATCH.","nullable":true},"notes":{"type":"string","description":"Free-form notes for this record.","nullable":true},"categories":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the category."},"description":{"type":"string","description":"Optional human-readable description.","nullable":true},"priority":{"type":"integer","description":"1-indexed sort position. Always equals (sorted index + 1) — see PATCH for details."},"destinationIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Destinations gated by this category when its logic evaluates to TRUE."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Condition tree evaluated against each inbound event. Write conditions that evaluate **TRUE for events you want to STOP**. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`); combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nDiscovery: `GET /rest/v1/mappings/default-variables` lists the canonical platform-provided `property` paths (visitor consent arrays, event fields, request context, identity fields). Custom `event.event_properties.*` paths are caller-defined.\n\nExample leaf (stop dispatch when the visitor rejected the `advertising` consent category): `{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }, { \"OR\": [/* nested logic nodes */] }] }`."}},"required":["name","priority"]},"description":"Governance categories in priority order (1..N)."}},"required":["id","kind","isEnabled","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchDataGovernance","tags":["Data Governance"],"x-stainless-method":"data_governance.update","security":[{"apiKey":[]}],"description":"Partially update the data-governance record. Top-level fields (`name`, `notes`, `isEnabled`) follow the standard PATCH semantic — only the fields you send are changed.\n\n`categories` is the documented exception: when sent, it is **replaced wholesale**. There is no partial-merge for individual categories. To change one category, fetch the current record, modify the array, and PATCH it back.\n\nCategories are sorted ascending by the `priority` you supplied and then renumbered `1..N` in the response, so the returned `priority` values are always sequential with no gaps. Stale `destinationIds` (deleted destinations or destinations on another account) are silently filtered out — the response echoes the filtered list, so a follow-up GET is not required to see what was saved. Requires scope: globalDispatch:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the category. Auto-generated if omitted.","nullable":true},"description":{"type":"string","description":"Optional human-readable description shown in the dashboard.","nullable":true},"destinationIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Destinations gated by this category. When the category logic evaluates to TRUE for an inbound event, dispatch to every destination on this list is stopped. Stale IDs (deleted destinations or destinations on another account) are silently filtered out at write time."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Condition tree evaluated against each inbound event. Write conditions that evaluate **TRUE for events you want to STOP**. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`); combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nDiscovery: `GET /rest/v1/mappings/default-variables` lists the canonical platform-provided `property` paths (visitor consent arrays, event fields, request context, identity fields). Custom `event.event_properties.*` paths are caller-defined.\n\nExample leaf (stop dispatch when the visitor rejected the `advertising` consent category): `{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }, { \"OR\": [/* nested logic nodes */] }] }`."},"priority":{"type":"number","description":"Used as a sort key on write. The server sorts categories by this value (ascending), then re-stamps priority as (sorted index + 1) on persist. Send any positive number — gaps are ironed out, duplicate values keep input order via stable sort. Omit to fall to the end.","nullable":true}}},"description":"Full replacement of the categories list. The sent array becomes the new state — there is no partial-merge semantic for categories. Categories are sorted by priority on write and re-stamped 1..N. Omit to leave existing categories untouched."},"isEnabled":{"type":"boolean","description":"Toggle data governance on/off without changing any other config.","nullable":true},"name":{"type":"string","description":"New display name for the record.","nullable":true},"notes":{"type":"string","description":"Replace the free-form notes.","nullable":true}}}}}},"responses":{"200":{"description":"Updated data-governance record","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this data-governance record."},"name":{"type":"string","description":"Human-readable name shown in the dashboard.","nullable":true},"kind":{"type":"string","description":"Discriminator for the entity type. Always \"dataGovernance\" on the REST surface. The underlying storage discriminator is \"globalDispatchCenter\" — REST translates it on the way out."},"isEnabled":{"type":"boolean","description":"When false, data governance is configured but does not gate dispatch — inbound events flow through to destinations regardless of category logic."},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the record was created."},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last write. Equal to createdAt on a freshly created record; advances on every PATCH.","nullable":true},"notes":{"type":"string","description":"Free-form notes for this record.","nullable":true},"categories":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the category."},"description":{"type":"string","description":"Optional human-readable description.","nullable":true},"priority":{"type":"integer","description":"1-indexed sort position. Always equals (sorted index + 1) — see PATCH for details."},"destinationIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Destinations gated by this category when its logic evaluates to TRUE."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Condition tree evaluated against each inbound event. Write conditions that evaluate **TRUE for events you want to STOP**. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`); combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nDiscovery: `GET /rest/v1/mappings/default-variables` lists the canonical platform-provided `property` paths (visitor consent arrays, event fields, request context, identity fields). Custom `event.event_properties.*` paths are caller-defined.\n\nExample leaf (stop dispatch when the visitor rejected the `advertising` consent category): `{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": { \"property\": \"visitor.consent.rejected_categories\", \"operator\": \"Contains\", \"value\": \"advertising\" } }, { \"OR\": [/* nested logic nodes */] }] }`."}},"required":["name","priority"]},"description":"Governance categories in priority order (1..N)."}},"required":["id","kind","isEnabled","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteDataGovernance","tags":["Data Governance"],"x-stainless-method":"data_governance.delete","security":[{"apiKey":[]}],"description":"Delete the data-governance record. After deletion, inbound events flow through to destinations without category-level gating. Create a new record with POST to reinstate governance. Requires scope: globalDispatch:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The id of the data-governance record that was deleted."},"deleted":{"type":"boolean","description":"True when the record was deleted."}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/replay-settings":{"get":{"operationId":"listReplaySettings","tags":["Replay Settings"],"x-stainless-method":"replay_settings.list","security":[{"apiKey":[]}],"description":"List the replay configurations on this account. Supports cursor pagination via `limit` and `cursor`. Replay settings control which domains may capture session replays and where the capture script is hosted. Requires scope: replaySettings:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"List of replay settings","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Stable identifier (UUID) for this replay configuration."},"name":{"type":"string","description":"Human-readable label for this replay configuration. Shown in the dashboard. May be empty."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Whether session replay capture is currently active. Set to \"Enabled\" to start capturing replays from whitelisted domains, or \"Disabled\" to pause capture without losing the configuration."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when this configuration was created."},"updatedAt":{"type":"string","description":"ISO-8601 timestamp of the most recent update, or null if never updated.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Hostnames where session replay capture is permitted. Replays initiated from any host not in this list are dropped. PATCH replaces the list — partial updates are not merged."},"customDomain":{"type":"string","description":"Optional custom domain (CNAME) for hosting the replay capture script. Leave null to use the default Ours Privacy domain.","nullable":true}},"required":["id","name","status","createdAt"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createReplaySettings","tags":["Replay Settings"],"x-stainless-method":"replay_settings.create","security":[{"apiKey":[]}],"description":"Create the replay configuration for this account. Each account is limited to one replay configuration — calls made when one already exists return HTTP 409 with the reason in the response `error` field. Requires scope: replaySettings:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customDomain":{"type":"string","description":"Optional custom domain (CNAME) for hosting the replay capture script. Leave null to use the default Ours Privacy domain.","nullable":true},"name":{"type":"string","description":"Human-readable label for this replay configuration. Shown in the dashboard. May be empty.","nullable":true},"status":{"type":"string","description":"Whether session replay capture is currently active. Set to \"Enabled\" to start capturing replays from whitelisted domains, or \"Disabled\" to pause capture without losing the configuration.","nullable":true,"enum":["Disabled","Enabled"]},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Hostnames where session replay capture is permitted. Replays initiated from any host not in this list are dropped. PATCH replaces the list — partial updates are not merged."}}}}}},"responses":{"200":{"description":"Created replay setting","content":{"application/json":{"schema":{"type":"object","properties":{"isSuccess":{"type":"boolean"},"cause":{"type":"string","nullable":true},"replaySettings":{"type":"object","nullable":true}},"required":["isSuccess"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — account already has a replay configuration. Each account is limited to one. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/replay-settings/{id}":{"get":{"operationId":"findReplaySettings","tags":["Replay Settings"],"x-stainless-method":"replay_settings.retrieve","security":[{"apiKey":[]}],"description":"Fetch a single replay configuration by ID, including its whitelisted domains and custom domain. Requires scope: replaySettings:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Replay Settings details","content":{"application/json":{"schema":{"type":"object","nullable":true,"properties":{"id":{"type":"string","description":"Stable identifier (UUID) for this replay configuration."},"name":{"type":"string","description":"Human-readable label for this replay configuration. Shown in the dashboard. May be empty."},"status":{"type":"string","enum":["Disabled","Enabled"],"description":"Whether session replay capture is currently active. Set to \"Enabled\" to start capturing replays from whitelisted domains, or \"Disabled\" to pause capture without losing the configuration."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when this configuration was created."},"updatedAt":{"type":"string","description":"ISO-8601 timestamp of the most recent update, or null if never updated.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Hostnames where session replay capture is permitted. Replays initiated from any host not in this list are dropped. PATCH replaces the list — partial updates are not merged."},"customDomain":{"type":"string","description":"Optional custom domain (CNAME) for hosting the replay capture script. Leave null to use the default Ours Privacy domain.","nullable":true}},"required":["id","name","status","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchReplaySettings","tags":["Replay Settings"],"x-stainless-method":"replay_settings.update","security":[{"apiKey":[]}],"description":"Update one or more fields on an existing replay configuration. Only the fields you send are changed; omitted fields keep their current value. Note that `whitelistDomains` is replaced wholesale (not merged with the existing list). Requires scope: replaySettings:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customDomain":{"type":"string","description":"Optional custom domain (CNAME) for hosting the replay capture script. Leave null to use the default Ours Privacy domain.","nullable":true},"name":{"type":"string","description":"Human-readable label for this replay configuration. Shown in the dashboard. May be empty.","nullable":true},"status":{"type":"string","description":"Whether session replay capture is currently active. Set to \"Enabled\" to start capturing replays from whitelisted domains, or \"Disabled\" to pause capture without losing the configuration.","nullable":true,"enum":["Disabled","Enabled"]},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Hostnames where session replay capture is permitted. Replays initiated from any host not in this list are dropped. PATCH replaces the list — partial updates are not merged."}}}}}},"responses":{"200":{"description":"Updated replay setting","content":{"application/json":{"schema":{"type":"object","properties":{"isSuccess":{"type":"boolean"},"cause":{"type":"string","nullable":true},"replaySettings":{"type":"object","nullable":true}},"required":["isSuccess"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteReplaySettings","tags":["Replay Settings"],"x-stainless-method":"replay_settings.delete","security":[{"apiKey":[]}],"description":"Delete the replay configuration. Capture stops immediately for all whitelisted domains. Requires scope: replaySettings:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"isSuccess":{"type":"boolean"},"cause":{"type":"string","nullable":true},"replaySettings":{"type":"object","nullable":true}},"required":["isSuccess"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/versions":{"get":{"operationId":"listVersions","tags":["Versions"],"x-stainless-method":"versions.list","security":[{"apiKey":[]}],"description":"List versions for this account, newest first. Supports cursor pagination and filtering by `isPublished`, `nameContains`, and `notesContains`. Combine filters with AND semantics. Requires scope: version:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"anyOf":[{"type":"string","enum":["true"]},{"type":"string","enum":["false"]}],"description":"Filter to only published or unpublished versions."},"required":false,"name":"isPublished","in":"query"},{"schema":{"type":"string","description":"Case-insensitive substring match on the version name."},"required":false,"name":"nameContains","in":"query"},{"schema":{"type":"string","description":"Case-insensitive substring match on the version notes."},"required":false,"name":"notesContains","in":"query"}],"responses":{"200":{"description":"List of versions","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"versionNumber":{"type":"number"},"isPublished":{"type":"boolean"},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string"},"publishedAt":{"type":"string","description":"When this version was most recently published. NOT cleared when a newer version is published — `publishedAt` reflects the most recent successful publish of this row, regardless of whether `isPublished` is currently true. Use `isPublished` to determine the current live version.","nullable":true},"publishedByUserId":{"type":"string","description":"Identifier of the user who most recently published this version.","nullable":true},"publishedByUserName":{"type":"string","description":"Display name of the user who most recently published this version.","nullable":true}},"required":["id","versionNumber","isPublished","createdAt"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createVersions","tags":["Versions"],"x-stainless-method":"versions.create","security":[{"apiKey":[]}],"description":"Publish the current draft of non-experiment entities (destinations, mappings, experiment settings, governance rules, etc.) as a new version. Newly created or modified DRAFT experiments and experiment variants are NOT shipped here — call `POST /rest/v1/experiments/{id}/start` instead, which atomically publishes the experiment and its variants by default (`publishAfterStart: true`). Returns the full Version on success. Returns HTTP 409 with the reason in the response `error` field when there are no draft changes to publish, when another publish is already in flight, or when the action otherwise conflicts with current state. To re-publish an existing version, use POST /rest/v1/versions/{id}/publish instead.\n\nSupports cherry-picking via the `include*` body fields. Each `include*` field is an entity-id allowlist for one collection — omit (or send `[]`) to include every draft change in that collection, or send a non-empty array to whitelist only those ids. Unlisted collections inherit wholesale from the latest published version. Requires scope: version:publish","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"includeAllowedEvents":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: allowed event ids (slug-like strings) to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeConsentSettings":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: consent settings ids to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeDestinations":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: destination UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeExternalAllowedEventData":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: external allowed event data ids to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeGlobalDispatchCenters":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: global dispatch center ids to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeMappings":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: mapping ids to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeReplaySettings":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: replay settings ids to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeSources":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: source UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeTagManagerTags":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: tag manager tag UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeTagManagerTriggers":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: tag manager trigger UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeTagManagerVariables":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: tag manager variable UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeDataGovernanceRules":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: data governance rule UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeDataGovernanceEvents":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: data governance event UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeExperimentSettings":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: experiment settings UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeExperiments":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: experiment UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"includeExperimentVariants":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Cherry-pick: experiment variant UUIDs to include from the draft. Omit or send `[]` to include all draft changes in this collection."},"name":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true}}}}}},"responses":{"200":{"description":"Created version","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"versionNumber":{"type":"number"},"isPublished":{"type":"boolean"},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string"},"publishedAt":{"type":"string","description":"When this version was most recently published. NOT cleared when a newer version is published — `publishedAt` reflects the most recent successful publish of this row, regardless of whether `isPublished` is currently true. Use `isPublished` to determine the current live version.","nullable":true},"publishedByUserId":{"type":"string","description":"Identifier of the user who most recently published this version.","nullable":true},"publishedByUserName":{"type":"string","description":"Display name of the user who most recently published this version.","nullable":true}},"required":["id","versionNumber","isPublished","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — no draft changes to publish, another publish is in flight, or otherwise cannot publish. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/versions/{id}":{"get":{"operationId":"findVersions","tags":["Versions"],"x-stainless-method":"versions.retrieve","security":[{"apiKey":[]}],"description":"Find a single version by ID. Requires scope: version:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Versions details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"versionNumber":{"type":"number"},"isPublished":{"type":"boolean"},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string"},"publishedAt":{"type":"string","description":"When this version was most recently published. NOT cleared when a newer version is published — `publishedAt` reflects the most recent successful publish of this row, regardless of whether `isPublished` is currently true. Use `isPublished` to determine the current live version.","nullable":true},"publishedByUserId":{"type":"string","description":"Identifier of the user who most recently published this version.","nullable":true},"publishedByUserName":{"type":"string","description":"Display name of the user who most recently published this version.","nullable":true}},"required":["id","versionNumber","isPublished","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchVersions","tags":["Versions"],"x-stainless-method":"versions.update","security":[{"apiKey":[]}],"description":"Partially update a version. Only the fields you send are changed. Requires scope: version:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true}}}}}},"responses":{"200":{"description":"Updated version","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"versionNumber":{"type":"number"},"isPublished":{"type":"boolean"},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string"},"publishedAt":{"type":"string","description":"When this version was most recently published. NOT cleared when a newer version is published — `publishedAt` reflects the most recent successful publish of this row, regardless of whether `isPublished` is currently true. Use `isPublished` to determine the current live version.","nullable":true},"publishedByUserId":{"type":"string","description":"Identifier of the user who most recently published this version.","nullable":true},"publishedByUserName":{"type":"string","description":"Display name of the user who most recently published this version.","nullable":true}},"required":["id","versionNumber","isPublished","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/versions/{id}/publish":{"post":{"operationId":"publishVersion","tags":["Versions"],"x-stainless-method":"versions.publish","security":[{"apiKey":[]}],"description":"Re-publish an existing (previously created) version by ID. Use this to roll back to an older snapshot. Returns 409 if the version is already published, was created more than 45 days ago, or another publish is already in flight. To create-and-publish from current draft state, use POST /rest/v1/versions instead. Requires scope: version:publish","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Republished version","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"versionNumber":{"type":"number"},"isPublished":{"type":"boolean"},"notes":{"type":"string","nullable":true},"createdAt":{"type":"string"},"publishedAt":{"type":"string","description":"When this version was most recently published. NOT cleared when a newer version is published — `publishedAt` reflects the most recent successful publish of this row, regardless of whether `isPublished` is currently true. Use `isPublished` to determine the current live version.","nullable":true},"publishedByUserId":{"type":"string","description":"Identifier of the user who most recently published this version.","nullable":true},"publishedByUserName":{"type":"string","description":"Display name of the user who most recently published this version.","nullable":true}},"required":["id","versionNumber","isPublished","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — version already published, older than 45 days, or another publish is in flight. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/versions/{id}/snapshot":{"get":{"operationId":"getVersionSnapshot","tags":["Versions"],"x-stainless-method":"versions.snapshot","security":[{"apiKey":[]}],"description":"Retrieve the full JSON snapshot captured by a version — every entity (destinations, sources, mappings, consent settings, etc.) as it existed when this version was published. Sensitive fields (API keys, tokens, secrets) are redacted. Useful for IaC export, audit, and backup workflows. Requires scope: version:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Version snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"versionNumber":{"type":"number"},"jsonContent":{"type":"object","description":"The full entity snapshot captured by this version. Keys are entity collection names (destinations, sources, allowedEvents, mappings, consentSettings, replaySettings, globalDispatchCenters, etc.) and values are arrays of the entities as they existed at publish time. Sensitive fields (API keys, tokens, secrets) are redacted. Returns null when the version snapshot has been pruned.","additionalProperties":true}},"required":["id","versionNumber","jsonContent"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/versions/{id}/diff":{"get":{"operationId":"getVersionDraftDiff","tags":["Versions"],"x-stainless-method":"versions.diff","security":[{"apiKey":[]}],"description":"Compare two versions of the account configuration. Returns added/removed/modified entities grouped by collection, plus a total `count`.\n\nDraft experiments and experiment variants do NOT appear in any diff — only experiments that are already running (or paused/completed) snapshot into versions. Use the experiment lifecycle endpoints (`/start`, `/stop`, etc.) to ship experiment changes; use these diff endpoints only for non-experiment entities (destinations, mappings, experiment settings, governance rules, etc.).\n\n- `GET /rest/v1/versions/draft/diff` — compare the current draft of non-experiment entities against the latest published version. Use this to preview what would be included in a `POST /rest/v1/versions` call. (`draft` is a literal path segment — there is no version with that ID; it identifies the comparison target.)\n- `GET /rest/v1/versions/{id}/diff` — compare that specific version against the latest published version.\n- `GET /rest/v1/versions/{id}/diff?against={otherId}` — compare two specific versions. `otherId` may also be `draft` to diff a published snapshot against the live draft state. Requires scope: version:find","parameters":[{"schema":{"anyOf":[{"type":"string","enum":["draft"]},{"type":"string","format":"uuid"}]},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Baseline version id to compare the path version against. Omit for the latest published version. Pass a version UUID to compute a version-vs-version diff."},"required":false,"name":"against","in":"query"}],"responses":{"200":{"description":"Diff between two versions","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","description":"Total number of differences across all entity collections. Equals the sum of `added + removed + modified` across every collection in `differences`."},"differences":{"type":"object","properties":{"destinations":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"sources":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"allowedEvents":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"externalAllowedEventData":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"mappings":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"consentSettings":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"globalDispatchCenters":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"replaySettings":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"tagManagers":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"tagManagerTags":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"tagManagerTriggers":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"tagManagerVariables":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"dataGovernanceRules":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"dataGovernanceEvents":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"experimentSettings":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"experiments":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]},"experimentVariants":{"type":"object","properties":{"added":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"removed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"modified":{"type":"array","items":{"type":"object","properties":{"old":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]},"new":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Human-readable label for the entity at the snapshot it was captured in. For most collections this is the entity's `name` field; for `allowedEvents` it is a computed summary of the event's key fields. Two `modified` items can therefore have identical `old.name` and `new.name` even though their underlying records differ — the change is in fields not surfaced by the summary. Use the `id` to fetch full detail.","nullable":true},"type":{"type":"string","description":"Entity subtype when the collection has one — for `sources` it matches the source's `type` field, letting clients distinguish kinds of source. `null` for collections without a subtype.","nullable":true},"summary":{"type":"string","description":"Optional change-classifier. Currently set to `'Reordered'` on `mappings` modifications when the only change is priority reordering — this lets clients de-emphasize them in change-review UI. `null` for every other diff item.","nullable":true},"tagManagerId":{"type":"string","description":"Parent tag-manager id for `tagManagerTags`, `tagManagerTriggers`, and `tagManagerVariables`. `null` for every other collection.","nullable":true}},"required":["id"]}},"required":["old","new"]},"description":"Entities present in both snapshots but with at least one field changed. `old` is the snapshot of the entity in the baseline (latest published); `new` is the snapshot in the comparison target (the draft)."}},"required":["added","removed","modified"]}},"required":["destinations","sources","allowedEvents","externalAllowedEventData","mappings","consentSettings","globalDispatchCenters","replaySettings","tagManagers","tagManagerTags","tagManagerTriggers","tagManagerVariables","dataGovernanceRules","dataGovernanceEvents","experimentSettings","experiments","experimentVariants"]}},"required":["count","differences"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/versions/{id}/revert":{"post":{"operationId":"revertVersions","tags":["Versions"],"x-stainless-method":"versions.revert","security":[{"apiKey":[]}],"description":"Revert one or more pending draft changes back to the latest published version (the \"abandon a change\" action). Send a body listing the entities to revert; each is handled independently — a modified entity is restored to its published values, a deleted entity is recreated, and a newly-added entity is discarded. An entity that already matches the published version is left untouched and is not counted (a no-op). An entity present in neither the draft nor the published version is reported in `notFoundCount`. `remainingChanges` is the total number of pending draft changes left after this call, which may stay above zero when other unreverted changes remain.\n\nThe `experiments` and `experimentVariants` collections are not supported here — use the experiment lifecycle endpoints (`/start`, `/stop`) for those — but `experimentSettings` is supported. To discard every pending change at once, use `POST /rest/v1/versions/draft/abandon`. (`draft` is a literal path segment identifying the live draft as the target.) Requires scope: version:publish","parameters":[{"schema":{"type":"string","enum":["draft"]},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"collection":{"type":"string","enum":["allowedEvents","consentSettings","dataGovernanceEvents","dataGovernanceRules","destinations","experimentSettings","externalAllowedEventData","globalDispatchCenters","mappings","replaySettings","sources","tagManagerTags","tagManagerTriggers","tagManagerVariables","tagManagers"],"description":"Entity collection the id belongs to. `experiments` and `experimentVariants` are not supported — use the experiment lifecycle endpoints for those — but `experimentSettings` is."},"id":{"type":"string","description":"Id of the draft entity to revert."}},"required":["collection","id"]},"description":"Draft entities to revert back to the latest published version. Each entry is processed independently: a modified entity is restored to its published values, a deleted entity is recreated, and a newly-added entity is discarded. Pass a single entry to revert one change."}},"required":["entities"]}}}},"responses":{"200":{"description":"Counts of reverted, restored, discarded, and not-found entities","content":{"application/json":{"schema":{"type":"object","properties":{"revertedCount":{"type":"number","description":"Entities overwritten back to their published values."},"restoredCount":{"type":"number","description":"Entities recreated because they had been deleted from the draft."},"discardedCount":{"type":"number","description":"Newly-added draft entities that were deleted."},"notFoundCount":{"type":"number","description":"Requested entities present in neither the draft nor the latest published version."},"remainingChanges":{"type":"number","description":"Total pending draft changes remaining after this operation."}},"required":["revertedCount","restoredCount","discardedCount","notFoundCount","remainingChanges"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/versions/{id}/abandon":{"post":{"operationId":"abandonVersions","tags":["Versions"],"x-stainless-method":"versions.abandon","security":[{"apiKey":[]}],"description":"Abandon ALL pending draft changes, resetting every supported entity collection back to the latest published version. Running experiments and experiment variants are not affected (ship those via the experiment lifecycle endpoints), but experiment settings are reset. Returns HTTP 409 when there is no published version to revert to, or when another publish or abandon is already in flight. To revert only specific changes, use `POST /rest/v1/versions/draft/revert`. (`draft` is a literal path segment identifying the live draft as the target.) Requires scope: version:publish","parameters":[{"schema":{"type":"string","enum":["draft"]},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Counts of reverted, restored, and discarded entities, plus `remainingChanges` (pending draft changes left afterward, normally 0). `notFoundCount` is always 0 for this action — it reverts the changes it discovers rather than a caller-supplied id list.","content":{"application/json":{"schema":{"type":"object","properties":{"revertedCount":{"type":"number","description":"Entities overwritten back to their published values."},"restoredCount":{"type":"number","description":"Entities recreated because they had been deleted from the draft."},"discardedCount":{"type":"number","description":"Newly-added draft entities that were deleted."},"notFoundCount":{"type":"number","description":"Requested entities present in neither the draft nor the latest published version."},"remainingChanges":{"type":"number","description":"Total pending draft changes remaining after this operation."}},"required":["revertedCount","restoredCount","discardedCount","notFoundCount","remainingChanges"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — there is no published version to revert to, or another publish/abandon is in flight. Reason is in the response `error`/`details` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments":{"get":{"operationId":"listExperiments","tags":["Experiments"],"x-stainless-method":"experiments.list","security":[{"apiKey":[]}],"description":"List experiments for this account. Each experiment includes its full `variants` array (redirect URLs and DOM modifications), so a single paginated call returns a complete client-side experiment config. Supports cursor pagination and filtering by `status`, `type`, and free-text `search` matched against experiment id, name, and description. Combine filters with AND semantics. Requires scope: experiment:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["completed","draft","paused","running"],"description":"Optional lifecycle-state filter."},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["ab","multivariate","personalization"],"description":"Optional experiment-type filter."},"required":false,"name":"type","in":"query"},{"schema":{"type":"string","description":"Optional case-insensitive text search matched against experiment ID, name, and description.","example":"pricing hero"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"List of experiments","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]},"description":"All persisted variants for this experiment, including the control variant. A non-personalization experiment needs at least two variants before it can be started. Reading variants is gated by the `experiment:find` permission: a key holding the `experiment:find` scope always receives them, and a key created by an org admin/member receives them too (that role satisfies the check). A key that holds neither the scope nor a qualifying creator role receives an empty array here."}},"required":["id","key","name","status","createdAt","trafficAllocation","variants"]},"description":"Experiments ordered by most recently updated first, then most recently created. Each experiment includes its full `variants` array (redirect URLs and DOM modifications), so a single paginated call returns a complete client-side experiment config."},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createExperiments","tags":["Experiments"],"x-stainless-method":"experiments.create","security":[{"apiKey":[]}],"description":"Create a new experiment. Requires scope: experiment:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"experimentSettingsId":{"type":"string","minLength":1,"description":"ID of the experiment settings record that owns this experiment. Call `GET /rest/v1/experiment-settings` first if you need to discover the correct ID for the current account.","example":"settings_01HZX9BB73EY2Q37VGK5A0VW7A"},"key":{"type":"string","description":"Optional stable code-facing key. When omitted, the API slugifies the name automatically.","nullable":true,"example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional hypothesis or operator note.","nullable":true},"type":{"type":"string","description":"Experiment mode to create. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting. Omit to create a standard `ab` experiment.","nullable":true,"enum":["ab","multivariate","personalization"]},"trafficAllocation":{"type":"number","description":"Initial traffic allocation percentage from 0 to 100.","nullable":true,"minimum":0,"maximum":100,"example":100},"includeQueryString":{"type":"boolean","description":"Whether redirect variants in this experiment should preserve the original request query string.","nullable":true},"controlWeight":{"type":"integer","description":"Weight of the auto-created control variant, as a percentage (1–100). Defaults to 100 (a new experiment is all control until treatments are added). The control must keep at least 1% — a 0% control leaves visitors with no bucket to assign at runtime. As treatments are added the control is reconciled to the remainder (100 − Σ treatment weights), so it normally does not need to be set explicitly.","nullable":true,"minimum":1,"maximum":100,"example":34},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules — URL patterns, audience, visitor status, query-param conditions. Omit to inherit defaults."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","nullable":true,"properties":{"eventName":{"type":"string","description":"Event name to use as the goal for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric should be derived from an existing funnel definition.","nullable":true}},"description":"Primary success metric. When provided, `eventName` must be a non-blank string."},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Event name to use as the goal for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric should be derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Goal events. If you send `metrics.primary`, `metrics.primary.eventName` must be a non-blank string. A primary event name is required before the experiment can be started."},"analysisConfig":{"type":"object","description":"Optional draft analysis method. Server-owned statistical constants are normalized before storage.","nullable":true}},"required":["experimentSettingsId","name"]}}}},"responses":{"200":{"description":"Created experiment","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]},"description":"All persisted variants for this experiment, including the control variant. A non-personalization experiment needs at least two variants before it can be started. Reading variants is gated by the `experiment:find` permission: a key holding the `experiment:find` scope always receives them, and a key created by an org admin/member receives them too (that role satisfies the check). A key that holds neither the scope nor a qualifying creator role receives an empty array here."}},"required":["id","key","name","status","createdAt","trafficAllocation","variants"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}":{"get":{"operationId":"findExperiments","tags":["Experiments"],"x-stainless-method":"experiments.retrieve","security":[{"apiKey":[]}],"description":"Find a single experiment by ID. Requires scope: experiment:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Experiments details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]},"description":"All persisted variants for this experiment, including the control variant. A non-personalization experiment needs at least two variants before it can be started. Reading variants is gated by the `experiment:find` permission: a key holding the `experiment:find` scope always receives them, and a key created by an org admin/member receives them too (that role satisfies the check). A key that holds neither the scope nor a qualifying creator role receives an empty array here."}},"required":["id","key","name","status","createdAt","trafficAllocation","variants"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchExperiments","tags":["Experiments"],"x-stainless-method":"experiments.update","security":[{"apiKey":[]}],"description":"Partially update an experiment. Only the fields you send are changed, except renaming a non-draft legacy experiment with no stored key preserves its current name-derived key. Edits are allowed on draft, running, and paused experiments and are recorded in the change log. Only completed experiments return 409 with `A completed experiment can no longer be edited`. Use the lifecycle endpoints (`/start`, `/pause`, `/resume`, `/stop`) to change status. Requires scope: experiment:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","description":"Updated stable code-facing key. When blank, the API falls back to a slugified key derived from the current experiment name. Renaming a non-draft legacy experiment with no stored key preserves its current name-derived key.","nullable":true},"name":{"type":"string","description":"Updated experiment name.","nullable":true},"description":{"type":"string","description":"Updated experiment hypothesis or operator note.","nullable":true},"trafficAllocation":{"type":"number","description":"Updated traffic allocation percentage from 0 to 100.","nullable":true,"minimum":0,"maximum":100},"includeQueryString":{"type":"boolean","description":"Updated redirect query-string forwarding behavior for the experiment.","nullable":true},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Updated eligibility rules. Send the full nested object — replaces the previous value, not merged."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","nullable":true,"properties":{"eventName":{"type":"string","description":"Event name to use as the goal for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric should be derived from an existing funnel definition.","nullable":true}},"description":"Primary success metric. When provided, `eventName` must be a non-blank string."},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Event name to use as the goal for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric should be derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Updated goal events. Send the full nested object — replaces the previous value, not merged. If you send `metrics.primary`, `metrics.primary.eventName` must be a non-blank string."},"analysisConfig":{"type":"object","description":"Updated draft analysis method. This field is locked once an experiment starts.","nullable":true}}}}}},"responses":{"200":{"description":"Updated experiment","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]},"description":"All persisted variants for this experiment, including the control variant. A non-personalization experiment needs at least two variants before it can be started. Reading variants is gated by the `experiment:find` permission: a key holding the `experiment:find` scope always receives them, and a key created by an org admin/member receives them too (that role satisfies the check). A key that holds neither the scope nor a qualifying creator role receives an empty array here."}},"required":["id","key","name","status","createdAt","trafficAllocation","variants"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteExperiments","tags":["Experiments"],"x-stainless-method":"experiments.delete","security":[{"apiKey":[]}],"description":"Delete an experiment. Requires scope: experiment:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"boolean","description":"Whether the experiment was deleted successfully.","example":true}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/duplicate":{"post":{"operationId":"duplicateExperiments","tags":["Experiments"],"x-stainless-method":"experiments.duplicate","security":[{"apiKey":[]}],"description":"Create a draft copy of an experiment. The copy keeps its configuration and variants, receives a new key, and does not retain lifecycle, rollout, or result state. Requires scope: experiment:create","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Duplicated experiment","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]},"description":"All persisted variants for this experiment, including the control variant. A non-personalization experiment needs at least two variants before it can be started. Reading variants is gated by the `experiment:find` permission: a key holding the `experiment:find` scope always receives them, and a key created by an org admin/member receives them too (that role satisfies the check). A key that holds neither the scope nor a qualifying creator role receives an empty array here."}},"required":["id","key","name","status","createdAt","trafficAllocation","variants"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Experiment not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/start":{"post":{"operationId":"startExperiment","tags":["Experiments"],"x-stainless-method":"experiments.start","security":[{"apiKey":[]}],"description":"Start an experiment. By default also publishes the experiment and its variants atomically as a new version, making them live for end users — this is the canonical publish path for experiment changes. They do NOT flow through `POST /rest/v1/versions`. Pass `{ \"publishAfterStart\": false }` only if a separate publish is desired (e.g. bundling with non-experiment edits via a manual `POST /rest/v1/versions` afterwards). The request body is optional — send `{}` to use defaults. Requires scope: experiment:start","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"publishAfterStart":{"type":"boolean","description":"When true (the default), atomically publish the experiment and its variants as a new version after starting — this is the canonical publish path for experiment changes. Any other unpublished non-experiment changes (destinations, mappings, settings, etc.) currently in draft are included in the same publish. Pass `false` explicitly to stage the change without publishing; the response will report `pending_publish` and a separate `POST /rest/v1/versions` call is then required.","example":true}}}}}},"responses":{"200":{"description":"Started experiment","content":{"application/json":{"schema":{"type":"object","properties":{"publishStatus":{"type":"string","enum":["pending_publish","published"],"description":"Whether the status change was also published to the current live version immediately."},"concurrentVersionChanges":{"type":"integer","description":"Number of unpublished version changes detected at the time of the lifecycle action. Values greater than 1 mean other unpublished work exists besides this experiment state change.","example":1},"experiment":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true}},"required":["id","key","name","status","createdAt","trafficAllocation"]}},"required":["publishStatus","concurrentVersionChanges","experiment"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/stop":{"post":{"operationId":"stopExperiment","tags":["Experiments"],"x-stainless-method":"experiments.stop","security":[{"apiKey":[]}],"description":"Stop an experiment. The request body is optional — send `{}` to stop without recording a winner. Optionally pass `winnerVariantId` to record the winner (reporting only) and/or `rolloutVariantId` to keep that variant serving to all traffic. Requires scope: experiment:stop","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"winnerVariantId":{"type":"string","description":"Optional declared winner to record (reporting metadata only — does not change what visitors are served).","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Optional variant to roll out to 100% of traffic immediately on stop — the explicit serving decision that keeps the winning experience live (a winning redirect becomes an ongoing redirect). Must be a non-control variant. Reverse later via `POST /experiments/{id}/end-rollout`.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"}},"description":"Stopping always publishes the new live config immediately so visitors stop being assigned — there is no `publishAfterStop` flag because deferring the publish would leave a completed experiment still serving traffic. Send `{}` to stop without a recorded winner; everyone returns to the original unless you also pass `rolloutVariantId`."}}}},"responses":{"200":{"description":"Stopped experiment","content":{"application/json":{"schema":{"type":"object","properties":{"publishStatus":{"type":"string","enum":["pending_publish","published"],"description":"Whether the status change was also published to the current live version immediately."},"concurrentVersionChanges":{"type":"integer","description":"Number of unpublished version changes detected at the time of the lifecycle action. Values greater than 1 mean other unpublished work exists besides this experiment state change.","example":1},"experiment":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true}},"required":["id","key","name","status","createdAt","trafficAllocation"]}},"required":["publishStatus","concurrentVersionChanges","experiment"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/rollout":{"post":{"operationId":"rolloutExperiments","tags":["Experiments"],"x-stainless-method":"experiments.rollout","security":[{"apiKey":[]}],"description":"Roll a non-control variant out to 100% of targeted traffic on a completed experiment — the explicit, reversible serving decision that makes a winning experience the ongoing default. Publishes. Reverse with `/end-rollout`. Returns 409 if the experiment is not completed or the variant is the control. Requires scope: experiment:stop","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"variantId":{"type":"string","description":"Non-control variant to roll out to 100% of targeted traffic. The runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Reverse via `POST /experiments/{id}/end-rollout`. The experiment must be completed.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"}},"required":["variantId"]}}}},"responses":{"200":{"description":"Rolled-out experiment","content":{"application/json":{"schema":{"type":"object","properties":{"publishStatus":{"type":"string","enum":["pending_publish","published"],"description":"Whether the status change was also published to the current live version immediately."},"concurrentVersionChanges":{"type":"integer","description":"Number of unpublished version changes detected at the time of the lifecycle action. Values greater than 1 mean other unpublished work exists besides this experiment state change.","example":1},"experiment":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true}},"required":["id","key","name","status","createdAt","trafficAllocation"]}},"required":["publishStatus","concurrentVersionChanges","experiment"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/end-rollout":{"post":{"operationId":"end-rolloutExperiments","tags":["Experiments"],"x-stainless-method":"experiments.endRollout","security":[{"apiKey":[]}],"description":"End an active rollout: stop serving the rolled-out variant so every matching visitor reverts to the original experience. The declared winner is left intact. Publishes. Returns 409 if the experiment is not currently rolled out. Requires scope: experiment:stop","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Experiment with the rollout cleared","content":{"application/json":{"schema":{"type":"object","properties":{"publishStatus":{"type":"string","enum":["pending_publish","published"],"description":"Whether the status change was also published to the current live version immediately."},"concurrentVersionChanges":{"type":"integer","description":"Number of unpublished version changes detected at the time of the lifecycle action. Values greater than 1 mean other unpublished work exists besides this experiment state change.","example":1},"experiment":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true}},"required":["id","key","name","status","createdAt","trafficAllocation"]}},"required":["publishStatus","concurrentVersionChanges","experiment"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/winner":{"post":{"operationId":"winnerExperiments","tags":["Experiments"],"x-stainless-method":"experiments.winner","security":[{"apiKey":[]}],"description":"Declare, change, or clear the winning variant on a completed experiment. Reporting metadata only — it does not change what visitors see and does not republish. Omit `winnerVariantId` to clear. To make the winner live, use `/rollout`. Requires scope: experiment:stop","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"winnerVariantId":{"type":"string","description":"Variant to record as the declared winner (reporting metadata only — does not change what visitors are served). Omit to clear the declared winner. The experiment must be completed.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"}},"description":"Declare, change, or clear the winning variant on a completed experiment. Reporting metadata only — it does not change what visitors see and does not republish. To make the winner live, roll it out via `POST /experiments/{id}/rollout`."}}}},"responses":{"200":{"description":"Experiment with the declared winner updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true}},"required":["id","key","name","status","createdAt","trafficAllocation"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/pause":{"post":{"operationId":"pauseExperiment","tags":["Experiments"],"x-stainless-method":"experiments.pause","security":[{"apiKey":[]}],"description":"Pause a running experiment. Stops new variant assignments while preserving existing ones; the experiment can later be resumed. The request body is optional. Requires scope: experiment:stop","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"publishAfterPause":{"type":"boolean","description":"When true (default on the REST surface), publish the current draft version immediately after pausing the experiment. Any other unpublished changes in the same account version are included in that publish. Pass `false` explicitly to stage the change without publishing; the response will report `pending_publish`.","example":true}}}}}},"responses":{"200":{"description":"Paused experiment","content":{"application/json":{"schema":{"type":"object","properties":{"publishStatus":{"type":"string","enum":["pending_publish","published"],"description":"Whether the status change was also published to the current live version immediately."},"concurrentVersionChanges":{"type":"integer","description":"Number of unpublished version changes detected at the time of the lifecycle action. Values greater than 1 mean other unpublished work exists besides this experiment state change.","example":1},"experiment":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true}},"required":["id","key","name","status","createdAt","trafficAllocation"]}},"required":["publishStatus","concurrentVersionChanges","experiment"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/resume":{"post":{"operationId":"resumeExperiment","tags":["Experiments"],"x-stainless-method":"experiments.resume","security":[{"apiKey":[]}],"description":"Resume a previously-paused experiment so new visitors can be assigned again. The request body is optional. Requires scope: experiment:start","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"publishAfterResume":{"type":"boolean","description":"When true (default on the REST surface), publish the current draft version immediately after resuming the experiment. Any other unpublished changes in the same account version are included in that publish. Pass `false` explicitly to stage the change without publishing; the response will report `pending_publish`.","example":true}}}}}},"responses":{"200":{"description":"Resumed experiment","content":{"application/json":{"schema":{"type":"object","properties":{"publishStatus":{"type":"string","enum":["pending_publish","published"],"description":"Whether the status change was also published to the current live version immediately."},"concurrentVersionChanges":{"type":"integer","description":"Number of unpublished version changes detected at the time of the lifecycle action. Values greater than 1 mean other unpublished work exists besides this experiment state change.","example":1},"experiment":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the experiment.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"key":{"type":"string","description":"Stable code-facing key for the experiment. Use this with the headless SDK `getExperimentByKey()` API instead of hard-coding opaque experiment IDs into application code.","example":"homepage-hero-headline-test"},"name":{"type":"string","description":"Short, human-readable experiment name.","example":"Homepage Hero Headline Test"},"description":{"type":"string","description":"Optional human-readable hypothesis or summary. In GraphQL this is backed by the experiment hypothesis field.","nullable":true,"example":"Test whether benefit-led copy increases demo requests."},"type":{"type":"string","description":"Experiment mode. `ab` and `multivariate` use traffic allocation and results; `personalization` is always-on targeting.","nullable":true,"example":"ab","enum":["ab","multivariate","personalization"]},"status":{"type":"string","enum":["completed","draft","paused","running"],"description":"Lifecycle state. `draft` is editable, `running` is active, `paused` is temporarily inactive, and `completed` is permanently stopped."},"createdAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was created.","example":"2026-04-30T18:42:11.219Z"},"updatedAt":{"type":"string","description":"ISO-8601 timestamp for the last persisted update, if any.","nullable":true},"startedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment most recently entered a running state.","nullable":true},"analysisStartedAt":{"type":"string","description":"ISO-8601 timestamp when the analysis plan was locked at experiment start.","nullable":true},"stoppedAt":{"type":"string","description":"ISO-8601 timestamp when the experiment was completed, if it has been stopped.","nullable":true},"trafficAllocation":{"type":"integer","description":"Percent of eligible traffic assigned into the experiment. Use 0 to fully disable enrollment without deleting the experiment.","example":100},"includeQueryString":{"type":"boolean","description":"For redirect variants, whether the original page query string should be forwarded onto the redirect URL.","nullable":true,"example":false},"winnerVariantId":{"type":"string","description":"Declared winning variant — reporting metadata only. Records which variant won; does NOT change what visitors are served. Set at stop time or later via `POST /experiments/{id}/winner`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"rolloutVariantId":{"type":"string","description":"Variant currently rolled out to 100% of targeted traffic on a completed experiment. When set (and not the control), the runtime keeps serving it to every matching visitor — a winning redirect becomes an ongoing redirect. Independent of `winnerVariantId`. Set via `POST /experiments/{id}/rollout`; cleared via `POST /experiments/{id}/end-rollout`.","nullable":true,"example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"targetingRules":{"type":"object","nullable":true,"properties":{"urlPatterns":{"type":"array","items":{"type":"string","maxLength":2000},"maxItems":200,"description":"Glob-style URL patterns that must match for the experiment to be eligible. Each pattern is either a path (`/pricing`, matched on any domain) or a host-qualified pattern (`get.example.com/pricing` or `https://get.example.com/pricing`, matched against the full URL so a single domain or subdomain can be targeted). Use `*` to match within a path segment and `**` to match across segments. Up to 200 patterns; each pattern up to 2000 characters. An empty array (or omitting the field) matches all URLs — equivalent to `['**']`. The host(s) targeted here must also appear in the parent experiment settings' `whitelistDomains` — that allowlist is what limits which domains can load your experiments (see `GET /experiment-settings`). If the host is missing, the SDK refuses to load there and the experiment never runs, even after `POST /experiments/{id}/start` succeeds.","example":["/pricing*","get.example.com/learn-more"]},"audienceId":{"type":"string","description":"Optional audience identifier used for server-side eligibility filtering.","nullable":true},"visitorProperties":{"type":"object","description":"Optional visitor-property matching rules. These are passed through as JSON for experimentation targeting.","nullable":true},"visitorStatus":{"type":"string","description":"Whether the experiment should target new visitors, returning visitors, or any visitor.","nullable":true},"queryParams":{"type":"array","nullable":true,"items":{"type":"object","properties":{"key":{"type":"string","description":"Query string key to inspect on the current page URL.","example":"utm_campaign"},"operator":{"type":"string","enum":["contains","equals","exists","not_equals","not_exists","regex"],"description":"Comparison operator applied to the query string value."},"value":{"type":"string","description":"Comparison value used by operators that require one. Omit for `exists` and `not_exists`.","nullable":true,"example":"spring-launch"}},"required":["key","operator"]},"description":"Additional query-string conditions that must all match for the visitor to qualify."}},"required":["urlPatterns"],"description":"Eligibility rules: URL-pattern globs, optional audience, query-param conditions, visitor status, and (server-side) visitor properties. Same shape as the create/patch input."},"metrics":{"type":"object","nullable":true,"properties":{"primary":{"type":"object","description":"Primary success metric used in the results report.","nullable":true},"secondary":{"type":"array","nullable":true,"items":{"type":"object","properties":{"eventName":{"type":"string","description":"Name of the event used to measure success for this metric.","nullable":true,"example":"demo_requested"},"funnelId":{"type":"string","description":"Optional funnel identifier when the metric is derived from an existing funnel definition.","nullable":true}}},"description":"Optional secondary metrics tracked alongside the primary goal."}},"description":"Configured success metrics. The read shape mirrors the write shape — `metrics` from a GET response can be PATCHed back without modification."},"analysisConfig":{"type":"object","description":"Normalized statistical analysis plan. Missing legacy rows resolve to Bayesian analysis in GraphQL.","nullable":true},"analysisMetricSnapshot":{"type":"object","description":"Primary metric definition frozen with the analysis plan at start time.","nullable":true}},"required":["id","key","name","status","createdAt","trafficAllocation"]}},"required":["publishStatus","concurrentVersionChanges","experiment"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/results":{"get":{"operationId":"getExperimentResults","tags":["Experiments"],"x-stainless-method":"experiments.results","security":[{"apiKey":[]}],"description":"Aggregate per-variant impressions, conversions, conversion rate, and Bayesian probability-to-be-best across the experiment runtime window. Requires scope: experiment:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Optional override for the conversion event name. When omitted, the experiment primary metric event is used.","example":"demo_requested"},"required":false,"name":"eventName","in":"query"}],"responses":{"200":{"description":"Aggregate experiment results","content":{"application/json":{"schema":{"type":"object","properties":{"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Variant ID this result row belongs to."},"name":{"type":"string","description":"Human-readable variant name as configured on the experiment (e.g. `Control`, `Treatment`). Lets callers label results without a separate lookup against `/experiment-variants`.","example":"Treatment"},"isControl":{"type":"boolean","description":"Whether this variant is the experiment control. Exactly one variant per experiment is the control.","example":false},"impressions":{"type":"integer","description":"Number of distinct experiment-impression events counted for the variant.","example":1842},"conversions":{"type":"integer","description":"Number of post-exposure conversions attributed to the variant.","example":213},"conversionRate":{"type":"number","description":"Conversions divided by impressions for this variant.","example":0.1156},"probabilityToBeBest":{"type":"number","description":"Bayesian probability that this variant is the best-performing option among all variants in the experiment.","example":0.87}},"required":["id","name","isControl","impressions","conversions","conversionRate","probabilityToBeBest"]},"description":"Aggregate performance metrics for each variant in the experiment over the experiment runtime window."}},"required":["variants"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/analysis":{"get":{"operationId":"getExperimentAnalysis","tags":["Experiments"],"x-stainless-method":"experiments.analysis","security":[{"apiKey":[]}],"description":"Return the configured Bayesian, fixed-horizon Frequentist, or Sequential analysis for conversion metrics; Bayesian and Frequentist also support value metrics. The response includes common visitor, impression, readiness, evidence, and data-quality fields plus the applicable method-specific result block. Visitors are the inferential unit; impressions remain a delivery diagnostic. Secondary event overrides are labeled exploratory, and unsupported legacy plans suppress official evidence. Requires scope: experiment:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Optional override for the conversion event name. When omitted, the experiment primary metric event is used.","example":"demo_requested"},"required":false,"name":"eventName","in":"query"}],"responses":{"200":{"description":"Method-aware experiment analysis","content":{"application/json":{"schema":{"type":"object","properties":{"method":{"type":"string","enum":["BAYESIAN","FREQUENTIST","SEQUENTIAL"]},"metricType":{"type":"string","enum":["conversion","value"]},"analysisState":{"type":"string","enum":["ANALYSIS_UNAVAILABLE","COLLECTING_DATA","FINALIZING_OUTCOMES","INCONCLUSIVE_STOPPED_EARLY","READY"]},"evidenceState":{"type":"string","nullable":true,"enum":["EVIDENCE_OF_HARM","EVIDENCE_OF_IMPROVEMENT","NO_CONCLUSION"]},"exploratory":{"type":"boolean"},"analysisCutoffAt":{"type":"string"},"exposureCutoffAt":{"type":"string"},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isControl":{"type":"boolean"},"visitors":{"type":"integer"},"observedVisitors":{"type":"integer"},"impressions":{"type":"integer"},"conversions":{"type":"integer"},"conversionRate":{"type":"number"},"meanValue":{"type":"number","nullable":true}},"required":["id","name","isControl","visitors","observedVisitors","impressions","conversions","conversionRate"]}},"bayesian":{"type":"object","nullable":true},"frequentist":{"type":"object","nullable":true},"sequential":{"type":"object","nullable":true},"dataQuality":{"type":"object","properties":{"crossoverVisitors":{"type":"integer"},"missingVisitorIdImpressions":{"type":"integer"},"sampleRatioMismatch":{"type":"string","enum":["FAIL","NOT_EVALUATED","PASS"]},"sampleRatioMismatchPValue":{"type":"number","nullable":true}},"required":["crossoverVisitors","missingVisitorIdImpressions","sampleRatioMismatch"]}},"required":["method","metricType","analysisState","exploratory","analysisCutoffAt","exposureCutoffAt","variants","dataQuality"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/results-time-series":{"get":{"operationId":"getExperimentResultsTimeSeries","tags":["Experiments"],"x-stainless-method":"experiments.resultsTimeSeries","security":[{"apiKey":[]}],"description":"Per-day per-variant impressions, conversions, and conversion rate, sliced to a date range. Use this to chart trends, compare windows, or zoom in on a specific period. Pass `startDate` / `endDate` (`YYYY-MM-DD`, UTC, both inclusive) to set the window; both default to the full experiment runtime when omitted, so the no-arg call returns every day from start to today (or to `stoppedAt` for completed experiments). The response orders days oldest-first and omits days with no impressions, so an empty `days` array means there was no measured traffic in the window. Requires scope: experiment:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Optional override for the conversion event name. When omitted, the experiment primary metric event is used.","example":"demo_requested"},"required":false,"name":"eventName","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the response window, as a UTC calendar day in `YYYY-MM-DD` format. Defaults to the experiment start date when omitted. Values before the experiment started are silently clamped to the experiment start.","example":"2026-04-01"},"required":false,"name":"startDate","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the response window, as a UTC calendar day in `YYYY-MM-DD` format. Defaults to the experiment stop date for completed experiments, or today for running experiments. Values after that are silently clamped. The window between `startDate` and `endDate` must be 366 days or fewer.","example":"2026-04-30"},"required":false,"name":"endDate","in":"query"}],"responses":{"200":{"description":"Time-series experiment results","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","description":"UTC calendar day in `YYYY-MM-DD` format.","example":"2026-04-30"},"variants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"impressions":{"type":"integer"},"conversions":{"type":"integer"},"conversionRate":{"type":"number"}},"required":["id","impressions","conversions","conversionRate"]}}},"required":["date","variants"]},"description":"Per-day metrics for each variant. Days with no impression rows are omitted from the response."}},"required":["days"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiments/{id}/session-replays":{"get":{"operationId":"getExperimentSessionReplays","tags":["Experiments"],"x-stainless-method":"experiments.sessionReplays","security":[{"apiKey":[]}],"description":"List session replays for sessions in which the `$experiment_impression` event fired for this experiment. Each row is one session with the variant the visitor was assigned for that impression. Sessions are ordered newest first by session start. Filter to one variant with `variant_id`. Cursor pagination via `limit` (1–100, default 25) and `cursor`; malformed cursors return 400. Requires scope: experiment:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","description":"Optional filter to a single variant ID. Returns sessions for all variants when omitted.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"required":false,"name":"variant_id","in":"query"}],"responses":{"200":{"description":"Session replays for visitors exposed to this experiment","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string","description":"Session identifier (`dp_sid`). Combine with `visitorId` and the `YYYY-MM-DD` date of `startTime` to deep-link to a replay outside of the response `replayUrl`.","example":"sess_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"visitorId":{"type":"string","description":"Visitor identifier whose session is recorded here.","example":"v_01HZX8TK6TSN91C5XWZP3N1M4E"},"variantName":{"type":"string","description":"Human-readable name of the variant the visitor was assigned for the recorded impression. May be empty if the variant has been deleted since the impression fired.","example":"Variant B"},"startTime":{"type":"string","description":"ISO-8601 timestamp of the first event recorded in this session. Sessions are ordered newest first by this field.","example":"2026-05-01T18:23:11.000Z"},"durationSeconds":{"type":"integer","description":"Time between the first and last events recorded for this session, in whole seconds.","example":184},"pageCount":{"type":"integer","description":"Number of distinct page paths visited during this session.","example":5},"replayUrl":{"type":"string","description":"Deep link to the in-app replay viewer for this session. Requires an authenticated session in the Ours Privacy app for an account that owns this experiment — not embeddable in customer-facing emails.","example":"https://app.oursprivacy.com/session-replay/replayer?sessionId=sess_01HZX8YJH3Z3W1R2Q4M5N6P7Q8&visitorId=v_01HZX8TK6TSN91C5XWZP3N1M4E&date=2026-05-01"}},"required":["sessionId","visitorId","variantName","startTime","durationSeconds","pageCount","replayUrl"]},"description":"Sessions in which a `$experiment_impression` event for this experiment was recorded, ordered newest first by session start time."},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiment-variants":{"get":{"operationId":"listExperimentVariants","tags":["Experiment Variants"],"x-stainless-method":"experiment_variants.list","security":[{"apiKey":[]}],"description":"List variants for a specific parent experiment. Requires the `experimentId` query parameter — variants are always scoped to a single experiment. Supports cursor pagination via `limit` and `cursor`; SDK runtimes that need the full set in one request can pass `?limit=100`. Requires scope: experiment:find","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of variants to return. Defaults to 200; values below 1 are clamped to 1 and values above 200 are clamped to 200. Variants per experiment are capped at 200 server-side, so a single request returns the full set.","example":200},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Required. List variants belonging to this parent experiment.","example":"08524dc8-5289-48e8-bf40-b3a7cfa6ca0a"},"required":true,"name":"experimentId","in":"query"}],"responses":{"200":{"description":"Experiment variants","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]},"description":"Variants on the parent experiment, including the auto-generated control. Returns an empty list when the experiment does not exist or is not visible to the caller. Variants per experiment are capped at 200 server-side."},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createExperimentVariants","tags":["Experiment Variants"],"x-stainless-method":"experiment_variants.create","security":[{"apiKey":[]}],"description":"Create a new experiment variant. Requires scope: experiment:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"experimentId":{"type":"string","minLength":1,"description":"Parent experiment ID that will own this new variant."},"name":{"type":"string","description":"Human-readable name for the new variant.","example":"Variant B"},"weight":{"type":"integer","minimum":0,"maximum":100,"description":"Traffic weight for this variant as a percentage (0–100). Treatment weights are percentages of the split and must total 99% or less to leave room for the control; the control variant is the remainder (100 − Σ treatment weights, always ≥ 1%) and is maintained automatically.","example":50},"variantType":{"type":"string","description":"Variant delivery mechanism. `dom_modifications` mutates the current page in-place at SDK runtime — use it for copy/style/image/HTML changes that keep visitors on the same URL (headline copy tests, button color, hero image swap). `redirect` routes the visitor to a different URL entirely — use it for landing-page A/B tests, alternate pricing pages, or any test where the *page itself* is the variable. They are not interchangeable: a redirect variant cannot also tweak DOM, and a dom_modifications variant cannot send the visitor elsewhere.","nullable":true,"enum":["dom_modifications","redirect"]},"redirectUrl":{"type":"string","description":"Required for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector for the element to modify at runtime. PREFER specific selectors that match exactly one element: an `id` (`#hero-headline`), a stable `data-*` attribute (`[data-testid=\"hero-headline\"]`), or a unique class/structural chain (`section.hero > h1.headline`). AVOID bare tag selectors like `h1`, `button`, or `img` — modern pages usually contain several, and the runtime applies the mutation to ONLY THE FIRST match, which silently picks the wrong element. If you only have a tag name, scope it with the nearest unique ancestor (e.g. `main h1`, `header nav a:first-of-type`).","example":"#hero-headline"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — or you can supply the structured `styles` / `attribute` field instead and the server will normalize.","nullable":true,"example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Use this for `setStyle` to avoid JSON-stringifying `{property: value}` yourself. Ignored for other actions."},"attribute":{"type":"object","description":"Use this for `setAttribute` to avoid JSON-stringifying `{name: value}` yourself. Ignored for other actions.","nullable":true}},"required":["selector","action"]},"description":"Required for DOM modification variants. Omit for redirect variants. Each entry is `{selector, action, value}`."},"isControl":{"type":"boolean","description":"Mark this variant as the experiment control. Defaults to `false`. The API rejects the request with 409 if the experiment already has a control variant. Every experiment keeps exactly one control whose weight is the auto-derived remainder of the split, so the control cannot be cleared while it would leave the treatments at 100% (no room for a control). DELETE on the control returns 409.","nullable":true,"example":false}},"required":["experimentId","name","weight"]}}}},"responses":{"200":{"description":"Created experiment variant","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiment-variants/{id}":{"get":{"operationId":"findExperimentVariants","tags":["Experiment Variants"],"x-stainless-method":"experiment_variants.retrieve","security":[{"apiKey":[]}],"description":"Find a single experiment variant by ID. Requires scope: experiment:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Experiment variant details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchExperimentVariants","tags":["Experiment Variants"],"x-stainless-method":"experiment_variants.update","security":[{"apiKey":[]}],"description":"Partially update an experiment variant. Only the fields you send are changed. Requires scope: experiment:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Updated variant name.","nullable":true},"weight":{"type":"integer","description":"Updated traffic weight as a percentage (0–100). The control variant weight is derived from the treatments (it is the remainder of the split) and cannot be set directly.","nullable":true,"minimum":0,"maximum":100,"example":50},"variantType":{"type":"string","description":"Updated variant delivery mechanism. `dom_modifications` mutates the current page in-place; `redirect` sends the visitor to a different URL — pick based on whether the *page* or the *content* is the variable. Changing this also requires updating the matching payload field (`redirectUrl` or `domModifications`).","nullable":true,"enum":["dom_modifications","redirect"]},"redirectUrl":{"type":"string","description":"Updated redirect URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected.","nullable":true},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector for the element to modify at runtime. PREFER specific selectors that match exactly one element: an `id` (`#hero-headline`), a stable `data-*` attribute (`[data-testid=\"hero-headline\"]`), or a unique class/structural chain (`section.hero > h1.headline`). AVOID bare tag selectors like `h1`, `button`, or `img` — modern pages usually contain several, and the runtime applies the mutation to ONLY THE FIRST match, which silently picks the wrong element. If you only have a tag name, scope it with the nearest unique ancestor (e.g. `main h1`, `header nav a:first-of-type`).","example":"#hero-headline"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — or you can supply the structured `styles` / `attribute` field instead and the server will normalize.","nullable":true,"example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Use this for `setStyle` to avoid JSON-stringifying `{property: value}` yourself. Ignored for other actions."},"attribute":{"type":"object","description":"Use this for `setAttribute` to avoid JSON-stringifying `{name: value}` yourself. Ignored for other actions.","nullable":true}},"required":["selector","action"]},"description":"Updated declarative DOM mutations. Sending this field replaces the prior list — partial-array merging is not supported."},"isControl":{"type":"boolean","description":"Promote or demote this variant as the control. Promoting a second variant while another already has `isControl: true` is rejected with 409. Demoting the control (`isControl: false`) is rejected when it would leave the treatments at 100% — there must always be room for a control.","nullable":true}}}}}},"responses":{"200":{"description":"Updated experiment variant","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteExperimentVariants","tags":["Experiment Variants"],"x-stainless-method":"experiment_variants.delete","security":[{"apiKey":[]}],"description":"Delete an experiment variant. Requires scope: experiment:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted experiment variant","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for this experiment variant.","example":"var_01HZX8YJH3Z3W1R2Q4M5N6P7Q8"},"experimentId":{"type":"string","description":"Parent experiment ID this variant belongs to.","example":"exp_01HZX8TK6TSN91C5XWZP3N1M4E"},"name":{"type":"string","description":"Human-readable variant name shown in the dashboard and results.","example":"Headline Variant B"},"isControl":{"type":"boolean","description":"Whether this is the baseline control variant.","example":false},"weight":{"type":"integer","description":"Relative traffic weight used when assigning visitors among variants in an active experiment.","example":50},"variantType":{"type":"string","description":"How this variant changes the user experience. `dom_modifications` for on-page changes or `redirect` for redirect tests.","nullable":true},"redirectUrl":{"type":"string","description":"Target URL for redirect variants. Use either a site-relative path such as `/pricing-v2` or an absolute `https://` URL. Cross-origin `http://` URLs are rejected. Omit for DOM modification variants.","nullable":true,"example":"https://www.example.com/pricing-v2"},"domModifications":{"type":"array","nullable":true,"items":{"type":"object","properties":{"selector":{"type":"string","description":"CSS selector used to find the element to modify on the page at runtime.","example":"h1.hero-title"},"action":{"type":"string","enum":["customCss","customJs","insertAfter","insertBefore","remove","setAttribute","setHtml","setImage","setStyle","setText"],"description":"Mutation to apply when the selector matches. Use `redirectUrl` instead of DOM modifications for redirect variants."},"value":{"type":"string","description":"Canonical action payload. For `setText` / `setHtml` / `customCss` / `customJs` / `setImage` / `insertBefore` / `insertAfter` this is the literal text/HTML/CSS/JS/URL. For `setStyle` and `setAttribute` it is a JSON-stringified `{key: value}` object — prefer the structured `styles` / `attribute` fields below to avoid manual JSON encoding.","example":"Start your free trial"},"styles":{"type":"array","nullable":true,"items":{"type":"object","properties":{"property":{"type":"string","minLength":1,"description":"CSS property name in camelCase or kebab-case.","example":"background-color"},"value":{"type":"string","description":"CSS value to assign to the property.","example":"#10B981"}},"required":["property","value"]},"description":"Populated on read for `setStyle` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`."},"attribute":{"type":"object","description":"Populated on read for `setAttribute` modifications, parsed from `value`. Customers may also send this field instead of a JSON-stringified `value` on write — see `domModificationInputSchema`.","nullable":true}},"required":["selector","action","value"]},"description":"Ordered list of declarative DOM mutations applied when this variant is assigned."}},"required":["id","experimentId","name","isControl","weight"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiment-settings":{"get":{"operationId":"listExperimentSettings","tags":["Experiment Settings"],"x-stainless-method":"experiment_settings.list","security":[{"apiKey":[]}],"description":"List experiment settings records for the account. Use the returned `id` as `experimentSettingsId` when creating an experiment. Requires scope: experimentSettings:list","responses":{"200":{"description":"List of experiment settings records","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the account-level experiment settings record.","example":"settings_01HZX9BB73EY2Q37VGK5A0VW7A"},"accountId":{"type":"string","description":"Account that owns this experiment settings record.","example":"org_01HZX8Y0YV9M7Y0F5Q1V3W4P6R"},"name":{"type":"string","description":"Human-readable name for this experimentation configuration.","example":"Default Web Experiment Settings"},"pixel":{"type":"string","description":"Pixel token used by the experiments runtime and CDN configuration. This is informative for REST clients; use the settings `id` for createExperiment."},"cookieName":{"type":"string","description":"Cookie name used to persist sticky variant assignments in the browser.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can load your experiments. When set, experiments using this settings record are only served on these domains; the SDK refuses to load anywhere else and your experiments never run on those hosts. Separate from source `whitelistDomains`, which limits which domains can send events to the CDP."},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","accountId","name","pixel","createdAt"]},"description":"Experiment settings records available to the current account. Use the `id` from this response as `experimentSettingsId` when creating an experiment. Most accounts have a single record; this list is not paginated."}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createExperimentSettings","tags":["Experiment Settings"],"x-stainless-method":"experiment_settings.create","security":[{"apiKey":[]}],"description":"Create the account-level experimentation bootstrap record. Most accounts should only ever have one. Requires scope: experimentSettings:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable name for this experimentation configuration. Defaults to `Experiment Settings` when omitted on create.","nullable":true,"example":"Default Web Experiment Settings"},"cookieName":{"type":"string","description":"Cookie name used to persist sticky variant assignments in the browser. Defaults to `_cord_exp` when omitted on create.","nullable":true,"example":"_cord_exp"},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can load your experiments. When set, experiments using this settings record are only served on these domains; the SDK refuses to load anywhere else and your experiments never run on those hosts. Separate from source `whitelistDomains`, which limits which domains can send events to the CDP.","example":["www.example.com","staging.example.com"]}}}}}},"responses":{"200":{"description":"Created experiment settings","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the account-level experiment settings record.","example":"settings_01HZX9BB73EY2Q37VGK5A0VW7A"},"accountId":{"type":"string","description":"Account that owns this experiment settings record.","example":"org_01HZX8Y0YV9M7Y0F5Q1V3W4P6R"},"name":{"type":"string","description":"Human-readable name for this experimentation configuration.","example":"Default Web Experiment Settings"},"pixel":{"type":"string","description":"Pixel token used by the experiments runtime and CDN configuration. This is informative for REST clients; use the settings `id` for createExperiment."},"cookieName":{"type":"string","description":"Cookie name used to persist sticky variant assignments in the browser.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can load your experiments. When set, experiments using this settings record are only served on these domains; the SDK refuses to load anywhere else and your experiments never run on those hosts. Separate from source `whitelistDomains`, which limits which domains can send events to the CDP."},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","accountId","name","pixel","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — experiment settings already exist for this account. Use PATCH on the existing record instead.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/experiment-settings/{id}":{"get":{"operationId":"findExperimentSettings","tags":["Experiment Settings"],"x-stainless-method":"experiment_settings.retrieve","security":[{"apiKey":[]}],"description":"Find a single experiment settings record by ID. Returns 404 when no record matches the supplied id. Requires scope: experimentSettings:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Experiment settings record","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the account-level experiment settings record.","example":"settings_01HZX9BB73EY2Q37VGK5A0VW7A"},"accountId":{"type":"string","description":"Account that owns this experiment settings record.","example":"org_01HZX8Y0YV9M7Y0F5Q1V3W4P6R"},"name":{"type":"string","description":"Human-readable name for this experimentation configuration.","example":"Default Web Experiment Settings"},"pixel":{"type":"string","description":"Pixel token used by the experiments runtime and CDN configuration. This is informative for REST clients; use the settings `id` for createExperiment."},"cookieName":{"type":"string","description":"Cookie name used to persist sticky variant assignments in the browser.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can load your experiments. When set, experiments using this settings record are only served on these domains; the SDK refuses to load anywhere else and your experiments never run on those hosts. Separate from source `whitelistDomains`, which limits which domains can send events to the CDP."},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","accountId","name","pixel","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchExperimentSettings","tags":["Experiment Settings"],"x-stainless-method":"experiment_settings.update","security":[{"apiKey":[]}],"description":"Partially update an experiment settings. Only the fields you send are changed. Requires scope: experimentSettings:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable name for this experimentation configuration. Defaults to `Experiment Settings` when omitted on create.","nullable":true,"example":"Default Web Experiment Settings"},"cookieName":{"type":"string","description":"Cookie name used to persist sticky variant assignments in the browser. Defaults to `_cord_exp` when omitted on create.","nullable":true,"example":"_cord_exp"},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can load your experiments. When set, experiments using this settings record are only served on these domains; the SDK refuses to load anywhere else and your experiments never run on those hosts. Separate from source `whitelistDomains`, which limits which domains can send events to the CDP.","example":["www.example.com","staging.example.com"]}}}}}},"responses":{"200":{"description":"Updated experiment settings","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the account-level experiment settings record.","example":"settings_01HZX9BB73EY2Q37VGK5A0VW7A"},"accountId":{"type":"string","description":"Account that owns this experiment settings record.","example":"org_01HZX8Y0YV9M7Y0F5Q1V3W4P6R"},"name":{"type":"string","description":"Human-readable name for this experimentation configuration.","example":"Default Web Experiment Settings"},"pixel":{"type":"string","description":"Pixel token used by the experiments runtime and CDN configuration. This is informative for REST clients; use the settings `id` for createExperiment."},"cookieName":{"type":"string","description":"Cookie name used to persist sticky variant assignments in the browser.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can load your experiments. When set, experiments using this settings record are only served on these domains; the SDK refuses to load anywhere else and your experiments never run on those hosts. Separate from source `whitelistDomains`, which limits which domains can send events to the CDP."},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","accountId","name","pixel","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteExperimentSettings","tags":["Experiment Settings"],"x-stainless-method":"experiment_settings.delete","security":[{"apiKey":[]}],"description":"Delete the experimentation bootstrap record. This also deletes child experiments, variants, and personalization properties owned by it. Requires scope: experimentSettings:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted experiment settings","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the account-level experiment settings record.","example":"settings_01HZX9BB73EY2Q37VGK5A0VW7A"},"accountId":{"type":"string","description":"Account that owns this experiment settings record.","example":"org_01HZX8Y0YV9M7Y0F5Q1V3W4P6R"},"name":{"type":"string","description":"Human-readable name for this experimentation configuration.","example":"Default Web Experiment Settings"},"pixel":{"type":"string","description":"Pixel token used by the experiments runtime and CDN configuration. This is informative for REST clients; use the settings `id` for createExperiment."},"cookieName":{"type":"string","description":"Cookie name used to persist sticky variant assignments in the browser.","nullable":true},"whitelistDomains":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Limits which domains can load your experiments. When set, experiments using this settings record are only served on these domains; the SDK refuses to load anywhere else and your experiments never run on those hosts. Separate from source `whitelistDomains`, which limits which domains can send events to the CDP."},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","accountId","name","pixel","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/mappings":{"get":{"operationId":"listMappings","tags":["Mappings"],"x-stainless-method":"mappings.list","security":[{"apiKey":[]}],"description":"List mappings for an entity (a source or destination). Requires the `entityId` query parameter. Supports cursor pagination via `limit` and `cursor`. Sorted by `priority` ascending, then by `id` for deterministic pagination. Requires scope: mapping:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of mappings to return. Defaults to 1000; values below 1 are clamped to 1 and values above 1000 are clamped to 1000. Most accounts can fetch the full list in one request.","example":1000},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","description":"Filter mappings by their parent entity id. Must be a destination id or source id.","example":"00000000-0000-0000-0000-000000000000"},"required":true,"name":"entityId","in":"query"}],"responses":{"200":{"description":"List of mappings","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createMappings","tags":["Mappings"],"x-stainless-method":"mappings.create","security":[{"apiKey":[]}],"description":"Create a mapping. Two body shapes are accepted:\n\n1. Quick-create — `{ allowedEventId, destinationId }`. Binds an allowed event to a destination. Returns a slim entity with empty `mappings[]`; follow up with PATCH to populate fields.\n2. Template fat-create — `{ entityId, templateId, mappings?, logic?, isEnabled?, name?, insertAfterIdx? }`. Lands a fully-shaped mapping in one round-trip. Use `GET /rest/v1/mapping-templates?entityId=...` to discover the valid `templateId` and `mappings[].property` values.\n\nSending both `allowedEventId` and `templateId` returns 400. Requires scope: mapping:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"allowedEventId":{"type":"string","description":"Quick-create variant: allowed event to bind the new mapping to. Required together with `destinationId`. Mutually exclusive with `templateId`/`entityId`."},"destinationId":{"type":"string","description":"Quick-create variant: destination that should receive events matched by this mapping. Required together with `allowedEventId`."},"entityId":{"type":"string","description":"Template fat-create variant: destination or source id this mapping belongs to. Required together with `templateId`."},"templateId":{"type":"string","description":"Template fat-create variant: template id from `GET /rest/v1/mapping-templates`. Picks the property descriptor set used to validate `mappings[].property`. Required together with `entityId`."},"insertAfterIdx":{"type":"integer","description":"Template fat-create only. Zero-based position in the destination/source priority order to insert this mapping after. Omit to append at the end."},"name":{"type":"string","description":"Template fat-create only. Override the auto-generated mapping name.","nullable":true},"isEnabled":{"type":"boolean","description":"Template fat-create only. Initial enabled state. Defaults to `true`."},"logic":{"type":"object","properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]},"description":"Template fat-create only. Optional initial property mappings. When omitted the mapping is seeded with template defaults for sources and non-default destination templates, and with `[]` for default destination templates."}},"description":"Two body shapes are accepted, distinguished by which keys are sent. Quick-create — `{ allowedEventId, destinationId }`. Template fat-create — `{ entityId, templateId, mappings?, logic?, isEnabled?, name?, insertAfterIdx? }`. Sending neither, both, or an incomplete pair returns 400 with a message telling you which variant to complete.","title":"CreateMappingRequest"}}}},"responses":{"200":{"description":"Created mapping","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}}}},"400":{"description":"Bad request — both `allowedEventId` and `templateId` were sent, or the body did not match either variant.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/mappings/{id}":{"get":{"operationId":"findMappings","tags":["Mappings"],"x-stainless-method":"mappings.retrieve","security":[{"apiKey":[]}],"description":"Find a single mapping by ID. Requires scope: mapping:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Mappings details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchMappings","tags":["Mappings"],"x-stainless-method":"mappings.update","security":[{"apiKey":[]}],"description":"Partially update a mapping. Only the fields you send are changed. Send `isEnabled: false` to pause the mapping without changing other fields (mirrors `status` on destinations). `mappings[]` is replaced wholesale when sent. Requires scope: mapping:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","nullable":true},"isEnabled":{"type":"boolean","description":"Flip the mapping on/off without changing other fields. `null` is treated as omitted.","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}}}}}},"responses":{"200":{"description":"Updated mapping","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteMappings","tags":["Mappings"],"x-stainless-method":"mappings.delete","security":[{"apiKey":[]}],"description":"Delete a mapping. Requires scope: mapping:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"boolean"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/mappings/reorder":{"post":{"operationId":"reorderMappings","tags":["Mappings"],"x-stainless-method":"mappings.reorder","security":[{"apiKey":[]}],"description":"Reassign `priority` for a set of mappings. Pass `{ uuids: [...] }` with the mapping ids in their new order — index 0 becomes the highest-priority mapping. All ids must belong to the same parent entity (source or destination); mixing entities returns 400. Requires scope: mapping:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"uuids":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Mapping ids in their new priority order, low priority index first. All ids must belong to the same parent entity (source or destination)."}},"required":["uuids"],"description":"Reassign `priority` for the supplied mappings. The position in `uuids` becomes the new priority order: index 0 is highest priority."}}}},"responses":{"200":{"description":"Reordered mappings","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}}},"required":["entities"]}}}},"400":{"description":"Bad request — `uuids` was empty, contained unknown ids, or referenced mappings across multiple parent entities.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/mappings/templates":{"get":{"operationId":"getMappingTemplates","tags":["Mappings"],"x-stainless-method":"mappings.templates","security":[{"apiKey":[]}],"description":"Discover every mapping template available for a destination or source, with full property descriptors inlined. Use the returned `id` as `templateId` when calling `POST /rest/v1/mappings` (template fat-create variant), and use each entry under `mappings[]` to learn the valid `property`, `kind`, `modificationOptions`, and any enforced `options`. The `isDefault: true` entry is the destination's built-in default template, configured via `PUT /rest/v1/default-mappings/{destinationId}`. Requires scope: mapping:find","parameters":[{"schema":{"type":"string","description":"Destination or source id. Required.","example":"00000000-0000-0000-0000-000000000000"},"required":true,"name":"entityId","in":"query"}],"responses":{"200":{"description":"Available mapping templates for an entity","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Template identifier — pass to `POST /rest/v1/mappings` as `templateId`."},"name":{"type":"string"},"description":{"type":"string","nullable":true},"isDefault":{"type":"boolean","description":"True for the destination's built-in default template (the one stored at `MAPPER#{destinationId}` when configured). Sources only have one template; it is always default."},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"The value to send as `mappings[].property` when creating or patching a mapping."},"label":{"type":"string","description":"Human-readable label (e.g. \"Email\", \"Event Name\")."},"description":{"type":"string","description":"Long-form description / tooltip for this property.","nullable":true},"kind":{"type":"string","enum":["Array","Boolean","Date","DateTime","Email","IP","Integer","JSON","KnownObject","Object","Text","Url"],"description":"Type information for SDK validation (Text, Integer, Email, Url, IP, Object, KnownObject, Date, DateTime, Array, Boolean, JSON)."},"isPII":{"type":"boolean"},"required":{"type":"boolean"},"map":{"type":"string","description":"The template default source expression, e.g. `{{visitor.email}}`."},"modification":{"type":"string","description":"The template default modification (hashing / case / URL truncation).","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]},"modificationOptions":{"type":"array","nullable":true,"items":{"type":"string","enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]},"description":"Suggested modification options for this property. Not a whitelist."},"options":{"type":"array","nullable":true,"items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]},"description":"When set, the ONLY valid `map` values for this property. Typically used for enum-shaped destinations."},"suggestedOptions":{"type":"array","nullable":true,"items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]},"description":"Non-binding suggestions for the `map` value (e.g. common event names a customer might want to use)."}},"required":["property","label","description","kind","isPII","required","map"]}}},"required":["id","name","isDefault","mappings"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/mappings/default-variables":{"get":{"operationId":"getMappingDefaultVariables","tags":["Mappings"],"x-stainless-method":"mappings.defaultVariables","security":[{"apiKey":[]}],"description":"Lists the platform-provided variables that any mapping `value` can reference (e.g. `event.email`, `event.request_context.ip`, `visitor.id`). Account-agnostic discovery — use these paths as the right-hand side of a mapping field. Requires scope: variables:find-default","responses":{"200":{"description":"Built-in variables available on every account","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Dot-path used in mapping `value` fields (e.g. `event.email`)."},"name":{"type":"string","description":"Human-readable display name."},"examples":{"type":"array","items":{"type":"string"},"description":"Sample values observed for this path (empty for unsampled defaults)."},"popularity":{"type":"number","description":"Relative popularity rank. Higher means more frequently set across events."},"advancedInfo":{"type":"string","description":"Optional long-form context shown in the variable dictionary drawer.","nullable":true}},"required":["path","name","examples","popularity"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/mappings/custom-variables":{"get":{"operationId":"getMappingCustomVariables","tags":["Mappings"],"x-stainless-method":"mappings.customVariables","security":[{"apiKey":[]}],"description":"Lists the custom variables observed in this account’s recent event stream (last 14 days). These are dot-paths under `event.event_properties.*` that callers can target in mapping `value` fields. Newly seen properties may take a few minutes to appear; an empty list means no custom properties have been observed yet for this account. Requires scope: variables:find-custom","responses":{"200":{"description":"Account-specific variables sampled from the last 14 days of events","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Dot-path used in mapping `value` fields (e.g. `event.email`)."},"name":{"type":"string","description":"Human-readable display name."},"examples":{"type":"array","items":{"type":"string"},"description":"Sample values observed for this path (empty for unsampled defaults)."},"popularity":{"type":"number","description":"Relative popularity rank. Higher means more frequently set across events."},"advancedInfo":{"type":"string","description":"Optional long-form context shown in the variable dictionary drawer.","nullable":true}},"required":["path","name","examples","popularity"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/mappings/modifications":{"get":{"operationId":"getMappingModifications","tags":["Mappings"],"x-stainless-method":"mappings.modifications","security":[{"apiKey":[]}],"description":"Lists every value accepted on a mapping field’s `modification` property, with a human-readable label and one-sentence description. Account-agnostic. Use this alongside `GET /rest/v1/mapping-templates` to render a labelled modification picker without hardcoding the enum. Requires scope: variables:find-default","responses":{"200":{"description":"Supported mapping modification types with descriptions","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string","enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"],"description":"Enum value to send on `modification` fields when authoring a mapping."},"label":{"type":"string","description":"Short human-readable name (suitable for picker labels)."},"description":{"type":"string","description":"One-sentence explanation of what the modification does to the mapped value."}},"required":["value","label","description"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/default-mappings":{"get":{"operationId":"listDefaultMappings","tags":["Default Mappings"],"x-stainless-method":"default_mappings.list","security":[{"apiKey":[]}],"description":"List every stored default mapping for the account, one per destination that has ever written a default. Destinations that have not yet written a default mapping do not appear here. Use `GET /rest/v1/default-mappings/{destinationId}` to fetch the hydrated would-be row for a specific destination. Requires scope: mapping:list","responses":{"200":{"description":"Default mapping rows that have been written for any destination","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/default-mappings/{id}":{"get":{"operationId":"findDefaultMappings","tags":["Default Mappings"],"x-stainless-method":"default_mappings.retrieve","security":[{"apiKey":[]}],"description":"Fetch the destination's default mapping by destination id. Returns a hydrated row with empty `mappings[]` when no default mapping has been written yet (so callers do not need to handle a 404-vs-200 branch). Each destination has at most one default mapping. Requires scope: mapping:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Default mapping row for the destination","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"put":{"operationId":"putDefaultMappings","tags":["Default Mappings"],"x-stainless-method":"default_mappings.replace","security":[{"apiKey":[]}],"description":"Upsert the destination default mapping. Always replaces `mappings[]` wholesale (default mappings have no merge-partial semantic). Default mappings cannot have custom `logic`; the field is not accepted on this endpoint. Requires scope: mapping:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]},"description":"Property mappings to persist as the destination default. Use `GET /rest/v1/mapping-templates?entityId={destinationId}` to discover the valid `property` values."},"isEnabled":{"type":"boolean","description":"Toggle the default mapping on/off. Defaults to `true` when omitted. `null` is treated as omitted.","nullable":true}},"required":["mappings"],"description":"Upsert the default mapping for a destination. Default mappings cannot have custom `logic` (the resolver rejects it), so the field is intentionally omitted here."}}}},"responses":{"200":{"description":"Upserted default mapping","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"isEnabled":{"type":"boolean"},"templateName":{"type":"string","nullable":true},"templateId":{"type":"string","nullable":true},"destinationId":{"type":"string","nullable":true},"sourceId":{"type":"string","nullable":true},"isDefaultMapping":{"type":"boolean","nullable":true},"priority":{"type":"number","nullable":true},"mappings":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string","description":"Destination-side field name. Comes from the destination template — discover the valid set via `GET /rest/v1/mapping-templates?entityId=...`."},"map":{"type":"string","description":"Source expression sent to the destination for this `property`. Use `{{...}}` template syntax to substitute values from the event/visitor record: `{{event.event}}`, `{{event.event_properties.value}}`, `{{visitor.email}}`. Bare strings (no `{{}}`) are sent verbatim. Note: `{{...}}` template syntax belongs HERE, NOT in `logic.condition.property` — logic conditions use bare dotted paths like `$event.event_properties.value`."},"modification":{"type":"string","nullable":true,"enum":["CamelCase","DmaIP","DomainOnly","DomainPathOnly","DomainPathUTMs","DomainUTMs","FakeDomain","FakeDomainRealPath","FakeIP","FullUrl","Hash","HashMD5","HashedCountry","HashedDateOfBirth","HashedGender","HashedNormalized","HashedNormalizedNoSpecialChars","HashedPhone","HashedState","HashedZip","KebabCase","LowerCase","None","Null","Redacted","RegionalIP","SnakeCase","StartCase","UpperCase"]}},"required":["property","map"]}},"logic":{"type":"object","description":"Condition tree gating when this mapping fires. A node is either a leaf `condition` or a combinator (`AND`, `OR`, `NOT`). Combinator children are themselves logic nodes, so trees nest arbitrarily.\n\nExample leaf: `{ \"condition\": { \"property\": \"$event.event\", \"operator\": \"Is\", \"value\": \"page_view\" } }`.\n\nExample combinator: `{ \"AND\": [{ \"condition\": ... }, { \"OR\": [...] }] }`."}},"required":["id","isEnabled","mappings"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/locations":{"get":{"operationId":"listLocations","tags":["Locations"],"x-stainless-method":"locations.list","security":[{"apiKey":[]}],"description":"List every location for this account. Not paginated — each account has a small map-count limit (single digits in practice) so the response always fits in a single page. Requires scope: maps:list","responses":{"200":{"description":"List of locations","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"mapName":{"type":"string","nullable":true},"customDomain":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"additionalAddresses":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true}}}},"center":{"type":"object","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createLocations","tags":["Locations"],"x-stainless-method":"locations.create","security":[{"apiKey":[]}],"description":"Create a new location (map embed). All address fields are optional and can be filled in later via PATCH. Returns the slim entity with the server-assigned `id` so callers can immediately request `GET /rest/v1/locations/{id}/embed-code`. Requires scope: maps:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mapName":{"type":"string","nullable":true},"customDomain":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"additionalAddresses":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number"},"longitude":{"type":"number"}},"required":["latitude","longitude"]}},"center":{"type":"object","nullable":true}}}}}},"responses":{"200":{"description":"Created location","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"mapName":{"type":"string","nullable":true},"customDomain":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"additionalAddresses":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true}}}},"center":{"type":"object","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — the account has reached its map count limit. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/locations/{id}":{"patch":{"operationId":"patchLocations","tags":["Locations"],"x-stainless-method":"locations.update","security":[{"apiKey":[]}],"description":"Partially update a location. Only the fields you send are changed. `additionalAddresses` is replaced wholesale when sent — partial item updates are not merged. The map's computed center is recalculated on every PATCH from the latest coordinates. Requires scope: maps:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mapName":{"type":"string","nullable":true},"customDomain":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"additionalAddresses":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number"},"longitude":{"type":"number"}},"required":["latitude","longitude"]}},"center":{"type":"object","nullable":true}}}}}},"responses":{"200":{"description":"Updated location","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"mapName":{"type":"string","nullable":true},"customDomain":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"additionalAddresses":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string","nullable":true},"line1":{"type":"string","nullable":true},"line2":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"zip":{"type":"string","nullable":true},"country":{"type":"string","nullable":true},"phoneNumber":{"type":"string","nullable":true},"websiteUrl":{"type":"string","nullable":true},"websiteLinkText":{"type":"string","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true}}}},"center":{"type":"object","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/locations/{id}/embed-code":{"get":{"operationId":"getLocationEmbedCode","tags":["Locations"],"x-stainless-method":"locations.embedCode","security":[{"apiKey":[]}],"description":"Generate the paste-ready HTML embed snippet for a location. The response is a single self-contained HTML string (a `<style>` block + `<div>` wrapping an `<iframe>` pointed at the Ours Privacy maps CDN, plus an optional JSON-LD `<script>`). Customize the render with the optional query params (`color`, `theme`, `colorScheme`, `mapStyle`, `includeAddressBox`, `zoom`, `includeControls`, `includeSEOSchema`); all have sane defaults. Requires scope: maps:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","default":"#007EA8","description":"Brand color used in the embedded map UI. Any CSS color string.","example":"#007EA8"},"required":false,"name":"color","in":"query"},{"schema":{"type":"string","enum":["default","modern"],"default":"default","description":"Visual theme variant."},"required":false,"name":"theme","in":"query"},{"schema":{"type":"string","enum":["light","dark"],"default":"light","description":"Light or dark color scheme."},"required":false,"name":"colorScheme","in":"query"},{"schema":{"type":"string","enum":["Standard","Monochrome"],"default":"Monochrome","description":"Base map style."},"required":false,"name":"mapStyle","in":"query"},{"schema":{"type":"boolean","description":"Render the address sidebar overlay next to the map. Send `false` to hide."},"required":false,"name":"includeAddressBox","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":20,"default":11,"description":"Initial map zoom level (Google-style 1–20)."},"required":false,"name":"zoom","in":"query"},{"schema":{"type":"string","enum":["yes","no","accessible"],"default":"yes","description":"Whether the embed renders map controls. `accessible` enables keyboard-navigable controls."},"required":false,"name":"includeControls","in":"query"},{"schema":{"type":"boolean","description":"Emit a `schema.org` Place JSON-LD block alongside the iframe so search engines can index the location."},"required":false,"name":"includeSEOSchema","in":"query"}],"responses":{"200":{"description":"Embed code HTML for the location","content":{"application/json":{"schema":{"type":"object","properties":{"embedCode":{"type":"string","description":"Self-contained HTML snippet (a `<style>` + `<div>` wrapping an `<iframe>`, plus an optional JSON-LD `<script>`) ready to paste into any page. The iframe `src` points to the maps CDN for the current stage."}},"required":["embedCode"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanners":{"get":{"operationId":"listWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.list","security":[{"apiKey":[]}],"description":"List every web scanner for this account. Not paginated — accounts have a small number of scanners in practice, so the response always fits in a single page. Requires scope: webScanner:list","responses":{"200":{"description":"List of web scanners","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"rootDomain":{"type":"string"},"excludedPatterns":{"type":"array","nullable":true,"items":{"type":"string"}},"includedUrls":{"type":"array","nullable":true,"items":{"type":"string"}},"urlLimit":{"type":"number","nullable":true},"scanSchedule":{"type":"string","enum":["daily","manual","monthly","weekly"]},"nextScheduledScanAt":{"type":"string","nullable":true},"scanStatus":{"type":"string","enum":["idle","scanning"]},"lastScannedAt":{"type":"string","nullable":true},"lastScanStartedAt":{"type":"string","nullable":true},"lastRunCookieCount":{"type":"number","nullable":true},"lastRunRequestCount":{"type":"number","nullable":true},"lastRunSuccessUrlCount":{"type":"number","nullable":true},"lastRunHighRiskRequestCount":{"type":"number","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","status","rootDomain","scanSchedule","scanStatus"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.create","security":[{"apiKey":[]}],"description":"Create a new web scanner for a root domain. A first scan is enqueued automatically after creation on a best-effort basis. `rootDomain` is required; missing, empty, or malformed values are rejected as HTTP 400. Everything else falls back to defaults (`status: Enabled`, `urlLimit: 100`, no excluded patterns, no extra seed URLs). The returned entity is the created scanner row and may not yet reflect async scan-state changes. Requires scope: webScanner:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rootDomain":{"type":"string","minLength":1,"description":"Root domain to crawl (e.g. `example.com`). Required on create. Missing or empty values fail request validation as HTTP 400. Present-but-malformed values are rejected as HTTP 400 with the validation reason in `details`."},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"excludedPatterns":{"type":"array","nullable":true,"items":{"type":"string"},"description":"URL glob patterns to skip during crawl. Max 100 entries."},"includedUrls":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Additional seed URLs to include as crawl entry points. Each must be an http(s) URL. Max 100 entries."},"urlLimit":{"type":"number","description":"Maximum URLs to crawl per scan (1–20,000). Defaults to 100 when omitted.","nullable":true},"scanSchedule":{"type":"string","enum":["daily","manual","monthly","weekly"],"description":"How often the scanner crawls this monitor on its own: `daily`, `weekly`, `monthly`, or `manual` to disable scheduled crawls and only run on demand. Defaults to `weekly`. Cadences advance on UTC calendar days from the last completed scan."}},"required":["rootDomain"]}}}},"responses":{"200":{"description":"Created web scanner","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"rootDomain":{"type":"string"},"excludedPatterns":{"type":"array","nullable":true,"items":{"type":"string"}},"includedUrls":{"type":"array","nullable":true,"items":{"type":"string"}},"urlLimit":{"type":"number","nullable":true},"scanSchedule":{"type":"string","enum":["daily","manual","monthly","weekly"]},"nextScheduledScanAt":{"type":"string","nullable":true},"scanStatus":{"type":"string","enum":["idle","scanning"]},"lastScannedAt":{"type":"string","nullable":true},"lastScanStartedAt":{"type":"string","nullable":true},"lastRunCookieCount":{"type":"number","nullable":true},"lastRunRequestCount":{"type":"number","nullable":true},"lastRunSuccessUrlCount":{"type":"number","nullable":true},"lastRunHighRiskRequestCount":{"type":"number","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","status","rootDomain","scanSchedule","scanStatus"]}}}},"400":{"description":"Bad request — the request body is malformed or `rootDomain` is missing, empty, or not a valid domain.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — one of the business-rule limits was exceeded (for example too many excluded patterns or `urlLimit` out of range). Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanners/{id}":{"get":{"operationId":"findWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.retrieve","security":[{"apiKey":[]}],"description":"Find a single web scanner by ID. Requires scope: webScanner:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Web scanner details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"rootDomain":{"type":"string"},"excludedPatterns":{"type":"array","nullable":true,"items":{"type":"string"}},"includedUrls":{"type":"array","nullable":true,"items":{"type":"string"}},"urlLimit":{"type":"number","nullable":true},"scanSchedule":{"type":"string","enum":["daily","manual","monthly","weekly"]},"nextScheduledScanAt":{"type":"string","nullable":true},"scanStatus":{"type":"string","enum":["idle","scanning"]},"lastScannedAt":{"type":"string","nullable":true},"lastScanStartedAt":{"type":"string","nullable":true},"lastRunCookieCount":{"type":"number","nullable":true},"lastRunRequestCount":{"type":"number","nullable":true},"lastRunSuccessUrlCount":{"type":"number","nullable":true},"lastRunHighRiskRequestCount":{"type":"number","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","status","rootDomain","scanSchedule","scanStatus"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.update","security":[{"apiKey":[]}],"description":"Partially update a web scanner. Only the fields you send are changed; omitted fields keep their current value. List-valued fields (`excludedPatterns`, `includedUrls`) are replaced wholesale when sent. If `rootDomain` is provided and malformed, the request is rejected as HTTP 400. Use `POST /rest/v1/web-scanners/{id}/trigger` to start a new scan after edits. Requires scope: webScanner:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rootDomain":{"type":"string","description":"Replace the scanner root domain. When provided, malformed values are rejected as HTTP 400 with the validation reason in `details`.","nullable":true},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"excludedPatterns":{"type":"array","nullable":true,"items":{"type":"string"}},"includedUrls":{"type":"array","nullable":true,"items":{"type":"string"}},"urlLimit":{"type":"number","nullable":true},"scanSchedule":{"type":"string","enum":["daily","manual","monthly","weekly"],"description":"How often the scanner crawls this monitor on its own: `daily`, `weekly`, `monthly`, or `manual` to disable scheduled crawls and only run on demand. Defaults to `weekly`. Cadences advance on UTC calendar days from the last completed scan. Omit to leave the current cadence unchanged."}}}}}},"responses":{"200":{"description":"Updated web scanner","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"rootDomain":{"type":"string"},"excludedPatterns":{"type":"array","nullable":true,"items":{"type":"string"}},"includedUrls":{"type":"array","nullable":true,"items":{"type":"string"}},"urlLimit":{"type":"number","nullable":true},"scanSchedule":{"type":"string","enum":["daily","manual","monthly","weekly"]},"nextScheduledScanAt":{"type":"string","nullable":true},"scanStatus":{"type":"string","enum":["idle","scanning"]},"lastScannedAt":{"type":"string","nullable":true},"lastScanStartedAt":{"type":"string","nullable":true},"lastRunCookieCount":{"type":"number","nullable":true},"lastRunRequestCount":{"type":"number","nullable":true},"lastRunSuccessUrlCount":{"type":"number","nullable":true},"lastRunHighRiskRequestCount":{"type":"number","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","status","rootDomain","scanSchedule","scanStatus"]}}}},"400":{"description":"Bad request — the request body is malformed or `rootDomain` is not a valid domain.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — a business-rule validation failed (for example oversized lists, `urlLimit` out of range, or a malformed seed URL). Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.delete","security":[{"apiKey":[]}],"description":"Delete a web scanner. Associated suppression rules are deleted in the same operation. Requires scope: webScanner:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The id of the web scanner that was deleted."},"deleted":{"type":"boolean","description":"True when the scanner and its rules were deleted."}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanners/{id}/trigger":{"post":{"operationId":"triggerWebScanner","tags":["Web Scanners"],"x-stainless-method":"web_scanners.trigger","security":[{"apiKey":[]}],"description":"Manually kick off a new scan for this web scanner. The request body is empty (or `{}`). A successful response means the request was accepted; because the scan starts asynchronously, the returned entity may still reflect pre-trigger values for fields like `scanStatus` and `lastScanStartedAt`. The trigger is rate-limited: a 409 is returned if another scan is already in flight, or if this production monitor has `urlLimit >= 5000` and its previous scan completed within the last 10 minutes; the reason is in the response `error` field. Requires scope: webScanner:trigger","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Web scanner details returned after the trigger request was accepted.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"rootDomain":{"type":"string"},"excludedPatterns":{"type":"array","nullable":true,"items":{"type":"string"}},"includedUrls":{"type":"array","nullable":true,"items":{"type":"string"}},"urlLimit":{"type":"number","nullable":true},"scanSchedule":{"type":"string","enum":["daily","manual","monthly","weekly"]},"nextScheduledScanAt":{"type":"string","nullable":true},"scanStatus":{"type":"string","enum":["idle","scanning"]},"lastScannedAt":{"type":"string","nullable":true},"lastScanStartedAt":{"type":"string","nullable":true},"lastRunCookieCount":{"type":"number","nullable":true},"lastRunRequestCount":{"type":"number","nullable":true},"lastRunSuccessUrlCount":{"type":"number","nullable":true},"lastRunHighRiskRequestCount":{"type":"number","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","status","rootDomain","scanSchedule","scanStatus"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — another scan is already running, or this production monitor has `urlLimit >= 5000` and its previous scan completed within the last 10 minutes. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanners/{id}/findings":{"get":{"operationId":"findingsWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.findings","security":[{"apiKey":[]}],"description":"List the third-party trackers (requests) found on a scan run, with their risk, category, the pages they were seen on, and whether each host is already covered by a CMP consent service. Defaults to the latest run; pass `date` (an ISO-8601 timestamp; only the calendar day is used to select the run) to read an earlier run. Documented exception to the cursor-pagination standard: paginates with `limit` and `offset` because each run is an immutable snapshot. A host that is neither covered (`coveredByCmp: false`) nor matched by a suppression rule still needs a triage decision — resolve it by adding the host to a CMP consent service or by creating a suppression rule with `POST /rest/v1/web-scanner-rules`. Use `GET /rest/v1/web-scanners/{id}/summary` for the rolled-up counts. Requires scope: webScanner:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"date-time","description":"Which scan run to read, as an ISO-8601 timestamp. Only the UTC calendar day is used to select the run; the time component is ignored. Defaults to the most recent run when omitted.","example":"2026-05-15T00:00:00Z"},"required":false,"name":"date","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Maximum number of findings to return. Defaults to 25; clamped to 1–100."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Skip this many findings before returning. Use with `limit` for load-more paging."},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Third-party trackers found on the selected scan run","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"hostname":{"type":"string"},"urls":{"type":"array","items":{"type":"string"}},"types":{"type":"array","items":{"type":"string","enum":["audio","beacon","document","eventsource","fedcm","fetch","font","image","manifest","media","other","ping","prefetch","script","stylesheet","texttrack","video","websocket","xhr"]}},"cookies":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string","nullable":true},"domain":{"type":"string","nullable":true},"path":{"type":"string","nullable":true}},"required":["name"]}},"seenOn":{"type":"array","items":{"type":"string"}},"privacyKeywords":{"type":"array","nullable":true,"items":{"type":"string"}},"risk":{"type":"string","nullable":true},"category":{"type":"string","nullable":true},"displayName":{"type":"string","nullable":true},"coveredByCmp":{"type":"boolean"},"coveredByVendorLabel":{"type":"string","nullable":true}},"required":["hostname","urls","types","seenOn","coveredByCmp"]},"description":"Third-party trackers seen on the run. `coveredByCmp` is true when the host is already mapped to a CMP consent service; `risk` is high/medium/low/unknown. Hosts that are neither covered nor matched by a suppression rule are the ones that need a triage decision — clear them by adding the host to a CMP consent service or creating a suppression rule (POST /rest/v1/web-scanner-rules)."},"total":{"type":"integer","description":"Total number of findings in the run."},"hasMore":{"type":"boolean","description":"True when more findings are available beyond the current window."}},"required":["items","total","hasMore"]}}}},"400":{"description":"Bad request — malformed `date`, or `limit`/`offset` out of range.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanners/{id}/cookies":{"get":{"operationId":"cookiesWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.cookies","security":[{"apiKey":[]}],"description":"List the cookies and local-storage entries observed on a scan run. Defaults to the latest run; pass `date` (an ISO-8601 timestamp; only the calendar day is used to select the run) to read an earlier run. Cookies paginate with `limit` and `offset` (a documented exception to the cursor-pagination standard, since each run is an immutable snapshot); local-storage entries are returned in full. Requires scope: webScanner:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"date-time","description":"Which scan run to read, as an ISO-8601 timestamp. Only the UTC calendar day is used to select the run; the time component is ignored. Defaults to the most recent run when omitted.","example":"2026-05-15T00:00:00Z"},"required":false,"name":"date","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Maximum number of findings to return. Defaults to 25; clamped to 1–100."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Skip this many findings before returning. Use with `limit` for load-more paging."},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Cookies and local-storage entries found on the selected scan run","content":{"application/json":{"schema":{"type":"object","properties":{"cookies":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string","nullable":true},"domain":{"type":"string","nullable":true},"path":{"type":"string","nullable":true}},"required":["name"]},"description":"Cookies observed on the run, paginated by `limit`/`offset`."},"localStorage":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string","nullable":true}},"required":["name"]},"description":"Local-storage entries observed on the run. Returned in full (not paginated)."},"totalCookies":{"type":"integer"},"totalLocalStorage":{"type":"integer"},"hasMore":{"type":"boolean","description":"True when more cookies are available beyond the current window."}},"required":["cookies","localStorage","totalCookies","totalLocalStorage","hasMore"]}}}},"400":{"description":"Bad request — malformed `date`, or `limit`/`offset` out of range.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanners/{id}/summary":{"get":{"operationId":"summaryWebScanners","tags":["Web Scanners"],"x-stainless-method":"web_scanners.summary","security":[{"apiKey":[]}],"description":"Compliance summary for a scan run — the rolled-up \"what does this site look like, and what still needs a decision\" view, assembled server-side so you do not have to page every finding. Includes total host/vendor/cookie counts, captured privacy policies and host coverage, a breakdown by risk and by category, coverage (how many hosts are already covered by a CMP consent service or a suppression rule vs. how many still need a decision), the new/removed host delta versus the previous run, and up to 10 highest-risk hosts that still need a decision. A null privacyPolicyUrl means that no policy was captured for the hostname. Defaults to the latest run; pass `date` (an ISO-8601 timestamp; only the calendar day is used to select the run) to read an earlier run. Clear a host that needs a decision by adding it to a CMP consent service or creating a suppression rule with `POST /rest/v1/web-scanner-rules`. When the scanner has no completed runs, every count is 0 and `runDate` is null. Requires scope: webScanner:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"date-time","description":"Which scan run to read, as an ISO-8601 timestamp. Only the UTC calendar day is used to select the run; the time component is ignored. Defaults to the most recent run when omitted.","example":"2026-05-15T00:00:00Z"},"required":false,"name":"date","in":"query"}],"responses":{"200":{"description":"Rolled-up compliance summary for the selected scan run","content":{"application/json":{"schema":{"type":"object","properties":{"scannerId":{"type":"string"},"rootDomain":{"type":"string"},"scanStatus":{"type":"string","enum":["idle","scanning"]},"runDate":{"type":"string","nullable":true},"hostCount":{"type":"integer"},"vendorCount":{"type":"integer"},"cookieCount":{"type":"integer"},"localStorageCount":{"type":"integer"},"privacyPolicyCount":{"type":"integer"},"privacyPolicies":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"hostnames":{"type":"array","items":{"type":"string"}},"text":{"type":"string"}},"required":["url","hostnames","text"]},"description":"Distinct privacy policies captured for first-party hostnames in the selected scan run, including the bounded visible policy text."},"privacyPolicyHosts":{"type":"array","items":{"type":"object","properties":{"hostname":{"type":"string"},"privacyPolicyUrl":{"type":"string","nullable":true}},"required":["hostname"]},"description":"Every successfully crawled first-party hostname and its captured privacy policy URL. A null privacyPolicyUrl means no policy was captured for that hostname."},"countsByRisk":{"type":"object","properties":{"high":{"type":"integer"},"medium":{"type":"integer"},"low":{"type":"integer"},"unknown":{"type":"integer"}},"required":["high","medium","low","unknown"]},"byCategory":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"hostCount":{"type":"integer"}},"required":["category","hostCount"]}},"coverage":{"type":"object","properties":{"totalHostCount":{"type":"integer"},"coveredHostCount":{"type":"integer"},"needsDecisionHostCount":{"type":"integer"},"coveragePercent":{"type":"integer"}},"required":["totalHostCount","coveredHostCount","needsDecisionHostCount","coveragePercent"]},"delta":{"type":"object","nullable":true,"properties":{"newHostCount":{"type":"integer"},"removedHostCount":{"type":"integer"}},"required":["newHostCount","removedHostCount"]},"topUncoveredHosts":{"type":"array","items":{"type":"object","properties":{"hostname":{"type":"string"},"urls":{"type":"array","items":{"type":"string"}},"types":{"type":"array","items":{"type":"string","enum":["audio","beacon","document","eventsource","fedcm","fetch","font","image","manifest","media","other","ping","prefetch","script","stylesheet","texttrack","video","websocket","xhr"]}},"cookies":{"type":"array","nullable":true,"items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string","nullable":true},"domain":{"type":"string","nullable":true},"path":{"type":"string","nullable":true}},"required":["name"]}},"seenOn":{"type":"array","items":{"type":"string"}},"privacyKeywords":{"type":"array","nullable":true,"items":{"type":"string"}},"risk":{"type":"string","nullable":true},"category":{"type":"string","nullable":true},"displayName":{"type":"string","nullable":true},"coveredByCmp":{"type":"boolean"},"coveredByVendorLabel":{"type":"string","nullable":true}},"required":["hostname","urls","types","seenOn","coveredByCmp"]},"description":"Up to 10 hosts that still need a decision (neither CMP-covered nor suppressed), highest risk first. Clear each by adding the host to a CMP consent service or creating a suppression rule (POST /rest/v1/web-scanner-rules) with the reason that explains why it is allowed (baa, internal, approved, compliant, firstParty, ignore)."},"accessibility":{"type":"object","nullable":true,"properties":{"engine":{"type":"string"},"score":{"type":"integer"},"pagesEvaluated":{"type":"integer"},"totalViolations":{"type":"integer"},"totalNodes":{"type":"integer"},"countsByImpact":{"type":"object","properties":{"critical":{"type":"integer"},"serious":{"type":"integer"},"moderate":{"type":"integer"},"minor":{"type":"integer"}},"required":["critical","serious","moderate","minor"]},"topViolations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"impact":{"type":"string","nullable":true},"help":{"type":"string"},"helpUrl":{"type":"string"},"wcagTags":{"type":"array","items":{"type":"string"}},"pageCount":{"type":"integer"},"nodeCount":{"type":"integer"},"pages":{"type":"array","items":{"type":"string"}},"sampleNodes":{"type":"array","items":{"type":"object","properties":{"target":{"type":"array","items":{"type":"string"}},"html":{"type":"string"},"failureSummary":{"type":"string","nullable":true}},"required":["target","html"]}}},"required":["id","help","helpUrl","wcagTags","pageCount","nodeCount","pages","sampleNodes"]}}},"required":["engine","score","pagesEvaluated","totalViolations","totalNodes","countsByImpact","topViolations"],"description":"Automated accessibility (WCAG 2.1/2.2 A + AA) rollup for the run: `score` is a 0-100 site score (mean of per-page scores; higher is better), with distinct rule-violation `countsByImpact` and the most frequently violated rules in `topViolations` (each with the number of pages it appears on). Covers only the machine-detectable subset of WCAG (~30-40%) — a high score is not a certification of full conformance; manual audit is still required. Null when the run audited no pages."}},"required":["scannerId","rootDomain","scanStatus","hostCount","vendorCount","cookieCount","localStorageCount","privacyPolicyCount","privacyPolicies","privacyPolicyHosts","countsByRisk","byCategory","coverage","topUncoveredHosts"]}}}},"400":{"description":"Bad request — malformed `date`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanner-rules":{"get":{"operationId":"listWebScannerRules","tags":["Web Scanner Rules"],"x-stainless-method":"web_scanner_rules.list","security":[{"apiKey":[]}],"description":"List suppression rules for a single web scanner. Requires the `scannerId` query parameter — rules are always scoped to a parent scanner. Not paginated; the per-scanner rule count is bounded. Requires scope: webScanner:find","parameters":[{"schema":{"type":"string","minLength":1,"description":"The web scanner whose suppression rules should be returned."},"required":true,"name":"scannerId","in":"query"}],"responses":{"200":{"description":"List of suppression rules for the requested scanner","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"scannerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"reason":{"type":"string","nullable":true,"enum":["approved","baa","compliant","firstParty","ignore","internal","other"]},"notes":{"type":"string","nullable":true},"priority":{"type":"integer"},"cookiePatterns":{"type":"array","items":{"type":"string"}},"domainPatterns":{"type":"array","items":{"type":"string"}},"scriptPatterns":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","scannerId","accountId","name","priority","cookiePatterns","domainPatterns","scriptPatterns","createdAt"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createWebScannerRules","tags":["Web Scanner Rules"],"x-stainless-method":"web_scanner_rules.create","security":[{"apiKey":[]}],"description":"Create a suppression rule on a web scanner. Auth is enforced against the parent scanner via `webScanner:update`. At least one of `cookiePatterns`, `domainPatterns`, or `scriptPatterns` should be set for the rule to match anything; omitted pattern arrays default to `[]`. Requires scope: webScanner:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scannerId":{"type":"string","minLength":1,"description":"The web scanner this rule belongs to."},"name":{"type":"string","minLength":1,"maxLength":500,"description":"User-friendly name for the suppression rule."},"priority":{"type":"integer","minimum":1,"maximum":10000,"description":"Rule priority (1–10,000). Lower numbers are evaluated first when multiple rules match."},"reason":{"type":"string","description":"Why this rule was added. Surfaced in audit views. Send `null` to clear an existing reason on patch.","nullable":true,"enum":["approved","baa","compliant","firstParty","ignore","internal","other"]},"notes":{"type":"string","description":"Free-form notes about why this rule exists or what it covers. Trimmed server-side; empty strings become `null`.","nullable":true},"cookiePatterns":{"type":"array","items":{"type":"string"},"maxItems":100,"description":"Glob patterns matched against cookie names (e.g. `_ga*`). Max 100 entries. When sent on PATCH, replaces the existing list wholesale."},"domainPatterns":{"type":"array","items":{"type":"string"},"maxItems":100,"description":"Glob patterns matched against cookie domain / script hostname (e.g. `*.google-analytics.com`). Max 100 entries. When sent on PATCH, replaces the existing list wholesale."},"scriptPatterns":{"type":"array","items":{"type":"string"},"maxItems":100,"description":"Glob patterns matched against full script URLs (e.g. `https://www.googletagmanager.com/gtm.js?id=*`). Max 100 entries. When sent on PATCH, replaces the existing list wholesale."}},"required":["scannerId","name","priority"]}}}},"responses":{"200":{"description":"Created suppression rule","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"scannerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"reason":{"type":"string","nullable":true,"enum":["approved","baa","compliant","firstParty","ignore","internal","other"]},"notes":{"type":"string","nullable":true},"priority":{"type":"integer"},"cookiePatterns":{"type":"array","items":{"type":"string"}},"domainPatterns":{"type":"array","items":{"type":"string"}},"scriptPatterns":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","scannerId","accountId","name","priority","cookiePatterns","domainPatterns","scriptPatterns","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-scanner-rules/{id}":{"get":{"operationId":"findWebScannerRules","tags":["Web Scanner Rules"],"x-stainless-method":"web_scanner_rules.retrieve","security":[{"apiKey":[]}],"description":"Find a single web scanner rule by ID. Requires scope: webScanner:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Suppression rule details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"scannerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"reason":{"type":"string","nullable":true,"enum":["approved","baa","compliant","firstParty","ignore","internal","other"]},"notes":{"type":"string","nullable":true},"priority":{"type":"integer"},"cookiePatterns":{"type":"array","items":{"type":"string"}},"domainPatterns":{"type":"array","items":{"type":"string"}},"scriptPatterns":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","scannerId","accountId","name","priority","cookiePatterns","domainPatterns","scriptPatterns","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchWebScannerRules","tags":["Web Scanner Rules"],"x-stainless-method":"web_scanner_rules.update","security":[{"apiKey":[]}],"description":"Partially update a suppression rule. Only the fields you send are changed. List-valued fields (`cookiePatterns`, `domainPatterns`, `scriptPatterns`) are replaced wholesale when sent. Requires scope: webScanner:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":500},"priority":{"type":"integer","minimum":1,"maximum":10000},"reason":{"type":"string","description":"Why this rule was added. Surfaced in audit views. Send `null` to clear an existing reason on patch.","nullable":true,"enum":["approved","baa","compliant","firstParty","ignore","internal","other"]},"notes":{"type":"string","description":"Free-form notes about why this rule exists or what it covers. Trimmed server-side; empty strings become `null`.","nullable":true},"cookiePatterns":{"type":"array","items":{"type":"string"},"maxItems":100,"description":"Glob patterns matched against cookie names (e.g. `_ga*`). Max 100 entries. When sent on PATCH, replaces the existing list wholesale."},"domainPatterns":{"type":"array","items":{"type":"string"},"maxItems":100,"description":"Glob patterns matched against cookie domain / script hostname (e.g. `*.google-analytics.com`). Max 100 entries. When sent on PATCH, replaces the existing list wholesale."},"scriptPatterns":{"type":"array","items":{"type":"string"},"maxItems":100,"description":"Glob patterns matched against full script URLs (e.g. `https://www.googletagmanager.com/gtm.js?id=*`). Max 100 entries. When sent on PATCH, replaces the existing list wholesale."}}}}}},"responses":{"200":{"description":"Updated suppression rule","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"scannerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"reason":{"type":"string","nullable":true,"enum":["approved","baa","compliant","firstParty","ignore","internal","other"]},"notes":{"type":"string","nullable":true},"priority":{"type":"integer"},"cookiePatterns":{"type":"array","items":{"type":"string"}},"domainPatterns":{"type":"array","items":{"type":"string"}},"scriptPatterns":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"createdByUserId":{"type":"string","nullable":true},"updatedByUserId":{"type":"string","nullable":true}},"required":["id","scannerId","accountId","name","priority","cookiePatterns","domainPatterns","scriptPatterns","createdAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteWebScannerRules","tags":["Web Scanner Rules"],"x-stainless-method":"web_scanner_rules.delete","security":[{"apiKey":[]}],"description":"Delete a web scanner rule. Requires scope: webScanner:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The id of the suppression rule that was deleted."},"deleted":{"type":"boolean","description":"True when the rule was deleted."}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/heatmap-pages":{"get":{"operationId":"listHeatmaps","tags":["Heatmaps"],"x-stainless-method":"heatmap_pages.list","security":[{"apiKey":[]}],"description":"List pages with heatmap coverage in a date window, ranked for triage. Each entity is identified by `pageKey`, normally origin + pathname with the query string stripped; an account-configured split may include one significant query parameter. Preserve the returned `pageKey` when calling `GET /rest/v1/heatmap-pages/summary`. Supports cursor pagination, with cursor depth capped at roughly 10,000 entries; if you need pages beyond that, narrow `from`/`to` or add filters rather than paginating further. `from`/`to` are UTC calendar days in `YYYY-MM-DD`; the window must be 60 days or fewer. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the heatmap window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-04-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the heatmap window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-04-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Filter by visitor country (ISO country name or code as stored on events)."},"required":false,"name":"country","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Filter by visitor region (state/province as stored on events)."},"required":false,"name":"region","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Filter by browser name as captured on events.","example":"Chrome"},"required":false,"name":"browserName","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":256,"description":"Case-insensitive substring match against `pageKey`.","example":"/pricing"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","enum":["CLICKS","DEAD_RATE","ISSUE_SCORE","RAGE_RATE"],"description":"Sort key. Defaults to `CLICKS` (descending)."},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["ASC","DESC"],"description":"Sort direction. Defaults to `DESC`."},"required":false,"name":"sortDir","in":"query"}],"responses":{"200":{"description":"Pages with heatmap coverage","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"pageKey":{"type":"string","description":"Stable per-page identifier. It is normally origin + pathname with the query string stripped, but an account-configured split may include one significant query parameter. Preserve the returned value when calling `GET /rest/v1/heatmap-pages/summary` and the heatmap MCP tools.","example":"https://example.com/pricing"},"totalClicks":{"type":"integer"},"rageClicks":{"type":"integer"},"deadClicks":{"type":"integer"},"rageRate":{"type":"number"},"deadRate":{"type":"number"},"issueScore":{"type":"integer"},"breakpoints":{"type":"array","items":{"type":"object","properties":{"breakpoint":{"type":"string"},"clicks":{"type":"integer"}},"required":["breakpoint","clicks"]}}},"required":["pageKey","totalClicks","rageClicks","deadClicks","rageRate","deadRate","issueScore","breakpoints"]},"description":"Pages with heatmap coverage in the requested window, ranked by the requested sort key (default: total clicks, descending). Use `pageKey` to drill into `GET /rest/v1/heatmap-pages/summary`."},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad request — malformed `from`/`to`, window longer than 60 days, or cursor depth exceeded.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/heatmap-pages/summary":{"get":{"operationId":"summaryHeatmaps","tags":["Heatmaps"],"x-stainless-method":"heatmap_pages.summary","security":[{"apiKey":[]}],"description":"Bundled per-page heatmap rollup: click bins, dead clicks, rage clicks, scroll depth, and up to 5 curated in-app replay URLs in a single payload. Designed as a one-call diagnosis surface for an AI assistant or marketer — answers \"what is happening on this page?\" without fanning out across five endpoints. `pageKey` comes from `GET /rest/v1/heatmap-pages`. The endpoint is identified by query params rather than a path id because heatmap pages are not stored entities; this is a documented derived-read exception. `breakpoint` scopes the bin/scroll aggregations; replays are returned across all breakpoints regardless of `breakpoint` (weighted to cover multiple viewports) so callers can compare devices. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":2048,"description":"Page identifier returned by `GET /rest/v1/heatmap-pages`. It is normally origin + pathname with the query string stripped, but may include one account-configured significant query parameter. Pass the returned value unchanged.","example":"https://example.com/pricing"},"required":true,"name":"pageKey","in":"query"},{"schema":{"type":"string","enum":["desktop","mobile","tablet"],"description":"Viewport bucket the click, dead-click, rage, and scroll-depth aggregations are computed for. Replays are returned for all breakpoints regardless of this value so callers can compare across devices."},"required":true,"name":"breakpoint","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the heatmap window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-04-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the heatmap window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-04-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Filter by visitor country (ISO country name or code as stored on events)."},"required":false,"name":"country","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Filter by visitor region (state/province as stored on events)."},"required":false,"name":"region","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128,"description":"Filter by browser name as captured on events.","example":"Chrome"},"required":false,"name":"browserName","in":"query"}],"responses":{"200":{"description":"Bundled heatmap rollup for one page","content":{"application/json":{"schema":{"type":"object","properties":{"clickBins":{"type":"array","items":{"type":"object","properties":{"binX":{"type":"integer"},"binY":{"type":"integer"},"clicks":{"type":"integer"}},"required":["binX","binY","clicks"]},"description":"Aggregated click positions for the requested breakpoint, bucketed into a 64×64 grid normalized to the page viewport."},"deadClicks":{"type":"array","items":{"type":"object","properties":{"binX":{"type":"integer"},"binY":{"type":"integer"},"deadClicks":{"type":"integer"},"topElement":{"type":"string","nullable":true}},"required":["binX","binY","deadClicks"]},"description":"Click positions where no interactive element was hit. `topElement` is the most-frequently-clicked non-interactive ancestor tag at that bucket, if attributable."},"rageClicks":{"type":"array","items":{"type":"object","properties":{"binX":{"type":"integer"},"binY":{"type":"integer"},"rageEvents":{"type":"integer"},"totalClicks":{"type":"integer"}},"required":["binX","binY","rageEvents","totalClicks"]},"description":"Click positions where repeated clicks were detected within a short window."},"scrollDepth":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"integer"},"sessions":{"type":"integer"}},"required":["bucket","sessions"]},"description":"Distribution of how far visitors scrolled, bucketed in percent of page height. `sessions` is the count of distinct sessions that reached at least that bucket."},"replays":{"type":"array","items":{"type":"object","properties":{"breakpoint":{"type":"string","enum":["desktop","mobile","tablet"],"description":"Viewport bucket the session was recorded under."},"url":{"type":"string","format":"uri","description":"Absolute link to view the session replay in the OursPrivacy web app. Opens the replayer pre-scoped to this session, visitor, and date.","example":"https://app.oursprivacy.com/session-replay/replayer?sessionId=sess_01HZX8YJH3Z3W1R2Q4M5N6P7Q8&visitorId=v_01HZX8TK6TSN91C5XWZP3N1M4E&date=2026-05-01"}},"required":["breakpoint","url"]},"maxItems":5,"description":"Up to 5 curated replay links for this page, ranked by engagement and weighted to cover multiple viewport breakpoints when possible. Each entry is an absolute URL into the OursPrivacy web app — open it to view the recorded session."}},"required":["clickBins","deadClicks","rageClicks","scrollDepth","replays"]}}}},"400":{"description":"Bad request — malformed `from`/`to`, missing `pageKey`/`breakpoint`, or window longer than 60 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-managers":{"get":{"operationId":"listTagManagers","tags":["Tag Managers"],"x-stainless-method":"tag_managers.list","security":[{"apiKey":[]}],"description":"List every tag manager on this account. Each tag manager is a pixel-scoped container of tags, triggers, variables, and folders. Not paginated — accounts are capped at a small number of tag managers in practice, so the response fits in a single page. Requires scope: tagManagers:list","responses":{"200":{"description":"List of tag managers","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag manager container."},"accountId":{"type":"string","description":"Account that owns this tag manager."},"name":{"type":"string","description":"Human-readable name for the tag manager."},"pixel":{"type":"string","description":"Server-assigned pixel/container token. Used in the tag-manager install snippet served on customer sites — do not regenerate via the API."},"dataLayerName":{"type":"string","description":"Window-global name of the customer data layer that triggers and variables read from (e.g. `dataLayer`). Defaults to `null`, which means the SDK falls back to its built-in name.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the tag manager was created.","nullable":true},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update.","nullable":true}},"required":["id","accountId","name","pixel"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createTagManagers","tags":["Tag Managers"],"x-stainless-method":"tag_managers.create","security":[{"apiKey":[]}],"description":"Create a new tag manager. The server seeds three default triggers (`Initialization`, `PageView`, `DomReady`) and one `OursInitTag` so the container is immediately usable — call `GET /tag-manager-triggers?tagManagerId={id}` right after create to grab their server-assigned ids so you can reuse them in `fireTriggerIds` instead of redundantly creating a second `PageView`/`DomReady`/`Initialization`. Returns the bare entity. Requires scope: tagManagers:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable name for the new tag manager."},"dataLayerName":{"type":"string","description":"Optional global data-layer name (e.g. `dataLayer`). Omit to use the SDK default.","nullable":true}},"required":["name"]}}}},"responses":{"200":{"description":"Created tag manager","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag manager container."},"accountId":{"type":"string","description":"Account that owns this tag manager."},"name":{"type":"string","description":"Human-readable name for the tag manager."},"pixel":{"type":"string","description":"Server-assigned pixel/container token. Used in the tag-manager install snippet served on customer sites — do not regenerate via the API."},"dataLayerName":{"type":"string","description":"Window-global name of the customer data layer that triggers and variables read from (e.g. `dataLayer`). Defaults to `null`, which means the SDK falls back to its built-in name.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the tag manager was created.","nullable":true},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update.","nullable":true}},"required":["id","accountId","name","pixel"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-managers/{id}":{"get":{"operationId":"findTagManagers","tags":["Tag Managers"],"x-stainless-method":"tag_managers.retrieve","security":[{"apiKey":[]}],"description":"Fetch a single tag manager by id, including its server-assigned `pixel` token used by the install snippet. Requires scope: tagManagers:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Tag manager details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag manager container."},"accountId":{"type":"string","description":"Account that owns this tag manager."},"name":{"type":"string","description":"Human-readable name for the tag manager."},"pixel":{"type":"string","description":"Server-assigned pixel/container token. Used in the tag-manager install snippet served on customer sites — do not regenerate via the API."},"dataLayerName":{"type":"string","description":"Window-global name of the customer data layer that triggers and variables read from (e.g. `dataLayer`). Defaults to `null`, which means the SDK falls back to its built-in name.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the tag manager was created.","nullable":true},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update.","nullable":true}},"required":["id","accountId","name","pixel"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchTagManagers","tags":["Tag Managers"],"x-stainless-method":"tag_managers.update","security":[{"apiKey":[]}],"description":"Partially update a tag manager. Only the fields you send are changed; omitted fields keep their current value. Send `dataLayerName: null` to clear the override and fall back to the SDK default. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New display name."},"dataLayerName":{"type":"string","description":"New data-layer name. Send `null` to clear and fall back to the SDK default.","nullable":true}}}}}},"responses":{"200":{"description":"Updated tag manager","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag manager container."},"accountId":{"type":"string","description":"Account that owns this tag manager."},"name":{"type":"string","description":"Human-readable name for the tag manager."},"pixel":{"type":"string","description":"Server-assigned pixel/container token. Used in the tag-manager install snippet served on customer sites — do not regenerate via the API."},"dataLayerName":{"type":"string","description":"Window-global name of the customer data layer that triggers and variables read from (e.g. `dataLayer`). Defaults to `null`, which means the SDK falls back to its built-in name.","nullable":true},"createdAt":{"type":"string","description":"ISO 8601 timestamp when the tag manager was created.","nullable":true},"updatedAt":{"type":"string","description":"ISO 8601 timestamp of the last update.","nullable":true}},"required":["id","accountId","name","pixel"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteTagManagers","tags":["Tag Managers"],"x-stainless-method":"tag_managers.delete","security":[{"apiKey":[]}],"description":"Delete a tag manager. Child tags, triggers, variables, and folders are cascade-deleted with the container. Requires scope: tagManagers:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The id of the tag manager that was deleted."},"deleted":{"type":"boolean","description":"True when the tag manager was deleted."}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-tags":{"get":{"operationId":"listTagManagerTags","tags":["Tag Manager Tags"],"x-stainless-method":"tag_manager_tags.list","security":[{"apiKey":[]}],"description":"List tags inside a single tag manager. Requires the `tagManagerId` query parameter — tags are always scoped to one parent container. Supports cursor pagination via `limit` and `cursor`; the limit clamp is 1000 so a single request can return the full set (the web-app workspace renders all tags in one shot). Requires scope: tagManagers:find","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of tags to return. Defaults to 25; values below 1 are clamped to 1 and values above 1000 are clamped to 1000. The web-app passes 1000 to render the full workspace in one request.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Parent tag manager whose tags should be returned."},"required":true,"name":"tagManagerId","in":"query"}],"responses":{"200":{"description":"Tags scoped to a parent tag manager","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag."},"tagManagerId":{"type":"string","description":"Parent tag manager that owns this tag."},"accountId":{"type":"string"},"name":{"type":"string","description":"Human-readable tag name."},"type":{"type":"string","description":"Tag type discriminator. Examples that exist today: `OursTrackTag`, `OursInitTag`, `OursIdentifyTag`, `CustomHtmlTag`. Pick from `GET /tag-manager-tags/types` for the canonical set — names like `GA4Event` are not valid ids."},"parameters":{"type":"object","description":"Type-specific JSON configuration. Shape depends on `type` — inspect a known-good tag of the same type for the field set. Empty object is valid for placeholder tags.","additionalProperties":true},"fireTriggerIds":{"type":"array","items":{"type":"string"},"description":"Triggers that fire this tag. Tag does nothing if this list is empty."},"blockTriggerIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Triggers that suppress this tag when they match — evaluated after fire triggers."},"priority":{"type":"number","description":"Lower numbers fire first. Defaults to 0.","nullable":true},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this tag belongs to in the dashboard. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","fireTriggerIds"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createTagManagerTags","tags":["Tag Manager Tags"],"x-stainless-method":"tag_manager_tags.create","security":[{"apiKey":[]}],"description":"Create a new tag inside a tag manager. `tagManagerId` is required in the body. Newly created tags are not assigned to any folder — assign them with `POST /rest/v1/tag-manager-asset-folders`. Requires scope: tagManagers:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tagManagerId":{"type":"string","minLength":1,"description":"Parent tag manager that will own the new tag."},"name":{"type":"string","description":"Human-readable tag name."},"type":{"type":"string","description":"Tag type discriminator. Pick from `GET /tag-manager-tags/types` for the canonical set (e.g. `OursTrackTag`, `OursInitTag`, `CustomHtmlTag`). Names like `GA4Event` are not valid ids."},"parameters":{"type":"object","description":"Type-specific JSON configuration. Send `{}` for a placeholder.","additionalProperties":true},"fireTriggerIds":{"type":"array","items":{"type":"string"},"description":"Trigger ids that cause this tag to fire. Use `[]` only for placeholder tags."},"blockTriggerIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Optional trigger ids that block this tag when they match."},"priority":{"type":"number","description":"Defaults to 0.","nullable":true},"enabled":{"type":"boolean","description":"Defaults to `true`.","nullable":true}},"required":["tagManagerId","name","type","parameters","fireTriggerIds"]}}}},"responses":{"200":{"description":"Created tag","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag."},"tagManagerId":{"type":"string","description":"Parent tag manager that owns this tag."},"accountId":{"type":"string"},"name":{"type":"string","description":"Human-readable tag name."},"type":{"type":"string","description":"Tag type discriminator. Examples that exist today: `OursTrackTag`, `OursInitTag`, `OursIdentifyTag`, `CustomHtmlTag`. Pick from `GET /tag-manager-tags/types` for the canonical set — names like `GA4Event` are not valid ids."},"parameters":{"type":"object","description":"Type-specific JSON configuration. Shape depends on `type` — inspect a known-good tag of the same type for the field set. Empty object is valid for placeholder tags.","additionalProperties":true},"fireTriggerIds":{"type":"array","items":{"type":"string"},"description":"Triggers that fire this tag. Tag does nothing if this list is empty."},"blockTriggerIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Triggers that suppress this tag when they match — evaluated after fire triggers."},"priority":{"type":"number","description":"Lower numbers fire first. Defaults to 0.","nullable":true},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this tag belongs to in the dashboard. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","fireTriggerIds"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-tags/{id}":{"get":{"operationId":"findTagManagerTags","tags":["Tag Manager Tags"],"x-stainless-method":"tag_manager_tags.retrieve","security":[{"apiKey":[]}],"description":"Fetch a single tag by id, including its `folderId` (read-only on this endpoint). Requires scope: tagManagers:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Tag details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag."},"tagManagerId":{"type":"string","description":"Parent tag manager that owns this tag."},"accountId":{"type":"string"},"name":{"type":"string","description":"Human-readable tag name."},"type":{"type":"string","description":"Tag type discriminator. Examples that exist today: `OursTrackTag`, `OursInitTag`, `OursIdentifyTag`, `CustomHtmlTag`. Pick from `GET /tag-manager-tags/types` for the canonical set — names like `GA4Event` are not valid ids."},"parameters":{"type":"object","description":"Type-specific JSON configuration. Shape depends on `type` — inspect a known-good tag of the same type for the field set. Empty object is valid for placeholder tags.","additionalProperties":true},"fireTriggerIds":{"type":"array","items":{"type":"string"},"description":"Triggers that fire this tag. Tag does nothing if this list is empty."},"blockTriggerIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Triggers that suppress this tag when they match — evaluated after fire triggers."},"priority":{"type":"number","description":"Lower numbers fire first. Defaults to 0.","nullable":true},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this tag belongs to in the dashboard. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","fireTriggerIds"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchTagManagerTags","tags":["Tag Manager Tags"],"x-stainless-method":"tag_manager_tags.update","security":[{"apiKey":[]}],"description":"Partially update a tag. Only the fields you send are changed. Tags cannot be moved between tag managers. To assign a tag to a folder, use `POST /rest/v1/tag-manager-asset-folders`. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Updated tag name."},"type":{"type":"string","description":"Updated tag type. Pick from `GET /tag-manager-tags/types`."},"parameters":{"type":"object","description":"Updated type-specific JSON configuration.","additionalProperties":true},"fireTriggerIds":{"type":"array","items":{"type":"string"},"description":"Replaces the fire trigger list wholesale."},"blockTriggerIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Replaces the block trigger list wholesale. Send `null` to clear."},"priority":{"type":"number","description":"Updated priority.","nullable":true},"enabled":{"type":"boolean","description":"Pause/resume the tag without changing other fields.","nullable":true}}}}}},"responses":{"200":{"description":"Updated tag","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Server-assigned UUID for this tag."},"tagManagerId":{"type":"string","description":"Parent tag manager that owns this tag."},"accountId":{"type":"string"},"name":{"type":"string","description":"Human-readable tag name."},"type":{"type":"string","description":"Tag type discriminator. Examples that exist today: `OursTrackTag`, `OursInitTag`, `OursIdentifyTag`, `CustomHtmlTag`. Pick from `GET /tag-manager-tags/types` for the canonical set — names like `GA4Event` are not valid ids."},"parameters":{"type":"object","description":"Type-specific JSON configuration. Shape depends on `type` — inspect a known-good tag of the same type for the field set. Empty object is valid for placeholder tags.","additionalProperties":true},"fireTriggerIds":{"type":"array","items":{"type":"string"},"description":"Triggers that fire this tag. Tag does nothing if this list is empty."},"blockTriggerIds":{"type":"array","nullable":true,"items":{"type":"string"},"description":"Triggers that suppress this tag when they match — evaluated after fire triggers."},"priority":{"type":"number","description":"Lower numbers fire first. Defaults to 0.","nullable":true},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this tag belongs to in the dashboard. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","fireTriggerIds"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteTagManagerTags","tags":["Tag Manager Tags"],"x-stainless-method":"tag_manager_tags.delete","security":[{"apiKey":[]}],"description":"Delete a tag manager tag. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-tags/types":{"get":{"operationId":"getTagManagerTagTypes","tags":["Tag Manager Tags"],"x-stainless-method":"tag_manager_tags.types","security":[{"apiKey":[]}],"description":"Lists every tag template the platform supports — what `type` discriminator to send on create/patch, and the shape of the type-specific `parameters` payload (fields, validators, required flags, available values for selects). Account-agnostic: the response is the same for every API key. The same registry powers server-side validation on `POST` / `PATCH` so what this endpoint advertises matches what the server enforces. Requires scope: tagManagers:find","responses":{"200":{"description":"Tag template descriptors","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Type discriminator — pass this as `type` on create/patch."},"name":{"type":"string","description":"Human-readable display name."},"category":{"type":"string","description":"Grouping label."},"description":{"type":"string","nullable":true},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Parameter key that goes in the `parameters` payload at create/patch."},"type":{"type":"string","enum":["STRING","BOOLEAN","INTEGER","FLOAT","TABLE"],"description":"Underlying data type of the parameter value."},"title":{"type":"string","description":"Human-readable title for the field."},"description":{"type":"string","nullable":true},"required":{"type":"boolean","nullable":true,"description":"When `true`, omitting or sending an empty value for this parameter on create/patch returns HTTP 400."},"default":{"type":"object","description":"Default value when the caller omits the parameter on create.","additionalProperties":true},"availableValues":{"type":"array","nullable":true,"items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"]},"description":"When present, the field accepts only one of these values. Send the `value` string in `parameters`; the `label` is for display."},"allowedKeys":{"type":"array","nullable":true,"items":{"type":"string"},"description":"For TABLE-typed fields, the predefined keys each row may contain."},"validators":{"type":"array","nullable":true,"items":{"type":"object","properties":{"type":{"type":"string","enum":["NotEmpty","CharacterLength","Url","Email","Number","Range"]},"min":{"type":"number","nullable":true},"max":{"type":"number","nullable":true}},"required":["type"]},"description":"Server-enforced rules applied to this field at create and patch."}},"required":["id","type","title"]}}},"required":["id","name","category","fields"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-triggers":{"get":{"operationId":"listTagManagerTriggers","tags":["Tag Manager Triggers"],"x-stainless-method":"tag_manager_triggers.list","security":[{"apiKey":[]}],"description":"List triggers inside a single tag manager. Requires the `tagManagerId` query parameter — triggers are always scoped to one parent container. Supports cursor pagination via `limit` and `cursor`; the limit clamp is 1000 so a single request can return the full set (the web-app workspace renders all triggers in one shot). Requires scope: tagManagers:find","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of triggers to return. Defaults to 25; values below 1 are clamped to 1 and values above 1000 are clamped to 1000. The web-app passes 1000 to render the full workspace in one request.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Parent tag manager whose triggers should be returned."},"required":true,"name":"tagManagerId","in":"query"}],"responses":{"200":{"description":"Triggers scoped to a parent tag manager","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Trigger type discriminator. Examples that exist today: `PageView`, `DomReady`, `Initialization`, `AllElementsClick`, `AllLinksClick`, `FormSubmit`, `CustomEvent`, `ScrollReach`, `Timer`. Pick from `GET /tag-manager-triggers/types` for the canonical set. Note there is no plain `Click` id; use one of the `All*Click` variants."},"parameters":{"type":"object","description":"Type-specific configuration. Send `{}` for a no-op trigger.","additionalProperties":true},"conditions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Conditions that must hold for the trigger to match. Use `[]` for unconditional triggers."},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this trigger belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","conditions"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createTagManagerTriggers","tags":["Tag Manager Triggers"],"x-stainless-method":"tag_manager_triggers.create","security":[{"apiKey":[]}],"description":"Create a new trigger inside a tag manager. `tagManagerId` is required in the body. Send `conditions: []` for an unconditional trigger; otherwise supply type-specific condition objects. Requires scope: tagManagers:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tagManagerId":{"type":"string","minLength":1,"description":"Parent tag manager that will own the new trigger."},"name":{"type":"string"},"type":{"type":"string","description":"Trigger type discriminator. Pick from `GET /tag-manager-triggers/types` for the canonical set (e.g. `PageView`, `CustomEvent`, `AllElementsClick`)."},"parameters":{"type":"object","description":"Type-specific JSON configuration.","additionalProperties":true},"conditions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Match conditions; use `[]` for an unconditional trigger. Conditions are evaluated by the Tag Manager runtime. For a CSS selector click condition, send `{ \"actual\": \"{{Click Element}}\", \"expected\": \".cta\", \"comparison\": \"match_css_selector\" }`."},"enabled":{"type":"boolean","description":"Defaults to `true`.","nullable":true}},"required":["tagManagerId","name","type","parameters","conditions"]}}}},"responses":{"200":{"description":"Created trigger","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Trigger type discriminator. Examples that exist today: `PageView`, `DomReady`, `Initialization`, `AllElementsClick`, `AllLinksClick`, `FormSubmit`, `CustomEvent`, `ScrollReach`, `Timer`. Pick from `GET /tag-manager-triggers/types` for the canonical set. Note there is no plain `Click` id; use one of the `All*Click` variants."},"parameters":{"type":"object","description":"Type-specific configuration. Send `{}` for a no-op trigger.","additionalProperties":true},"conditions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Conditions that must hold for the trigger to match. Use `[]` for unconditional triggers."},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this trigger belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","conditions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-triggers/{id}":{"get":{"operationId":"findTagManagerTriggers","tags":["Tag Manager Triggers"],"x-stainless-method":"tag_manager_triggers.retrieve","security":[{"apiKey":[]}],"description":"Find a single tag manager trigger by ID. Requires scope: tagManagers:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Trigger details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Trigger type discriminator. Examples that exist today: `PageView`, `DomReady`, `Initialization`, `AllElementsClick`, `AllLinksClick`, `FormSubmit`, `CustomEvent`, `ScrollReach`, `Timer`. Pick from `GET /tag-manager-triggers/types` for the canonical set. Note there is no plain `Click` id; use one of the `All*Click` variants."},"parameters":{"type":"object","description":"Type-specific configuration. Send `{}` for a no-op trigger.","additionalProperties":true},"conditions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Conditions that must hold for the trigger to match. Use `[]` for unconditional triggers."},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this trigger belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","conditions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchTagManagerTriggers","tags":["Tag Manager Triggers"],"x-stainless-method":"tag_manager_triggers.update","security":[{"apiKey":[]}],"description":"Partially update a trigger. Only the fields you send are changed. `conditions` is replaced wholesale when sent. To assign a trigger to a folder, use `POST /rest/v1/tag-manager-asset-folders`. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Updated trigger name."},"type":{"type":"string","description":"Updated trigger type. Pick from `GET /tag-manager-triggers/types`."},"parameters":{"type":"object","description":"Updated type-specific JSON configuration.","additionalProperties":true},"conditions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Replaces conditions wholesale when sent. Use `[]` for an unconditional trigger."},"enabled":{"type":"boolean","description":"Pause/resume the trigger without changing other fields.","nullable":true}}}}}},"responses":{"200":{"description":"Updated trigger","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Trigger type discriminator. Examples that exist today: `PageView`, `DomReady`, `Initialization`, `AllElementsClick`, `AllLinksClick`, `FormSubmit`, `CustomEvent`, `ScrollReach`, `Timer`. Pick from `GET /tag-manager-triggers/types` for the canonical set. Note there is no plain `Click` id; use one of the `All*Click` variants."},"parameters":{"type":"object","description":"Type-specific configuration. Send `{}` for a no-op trigger.","additionalProperties":true},"conditions":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Conditions that must hold for the trigger to match. Use `[]` for unconditional triggers."},"enabled":{"type":"boolean","description":"Defaults to `true` on create.","nullable":true},"folderId":{"type":"string","description":"Folder this trigger belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters","conditions"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteTagManagerTriggers","tags":["Tag Manager Triggers"],"x-stainless-method":"tag_manager_triggers.delete","security":[{"apiKey":[]}],"description":"Delete a tag manager trigger. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-triggers/types":{"get":{"operationId":"getTagManagerTriggerTypes","tags":["Tag Manager Triggers"],"x-stainless-method":"tag_manager_triggers.types","security":[{"apiKey":[]}],"description":"Lists every trigger template the platform supports — what `type` discriminator to send on create/patch, and the shape of the type-specific `parameters` payload. Trigger `conditions` are evaluated at runtime (per-trigger, see the resource docs) and are not part of this descriptor. Account-agnostic: the response is the same for every API key. Requires scope: tagManagers:find","responses":{"200":{"description":"Trigger template descriptors","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Type discriminator — pass this as `type` on create/patch."},"name":{"type":"string","description":"Human-readable display name."},"category":{"type":"string","description":"Grouping label."},"description":{"type":"string","nullable":true},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Parameter key that goes in the `parameters` payload at create/patch."},"type":{"type":"string","enum":["STRING","BOOLEAN","INTEGER","FLOAT","TABLE"],"description":"Underlying data type of the parameter value."},"title":{"type":"string","description":"Human-readable title for the field."},"description":{"type":"string","nullable":true},"required":{"type":"boolean","nullable":true,"description":"When `true`, omitting or sending an empty value for this parameter on create/patch returns HTTP 400."},"default":{"type":"object","description":"Default value when the caller omits the parameter on create.","additionalProperties":true},"availableValues":{"type":"array","nullable":true,"items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"]},"description":"When present, the field accepts only one of these values. Send the `value` string in `parameters`; the `label` is for display."},"allowedKeys":{"type":"array","nullable":true,"items":{"type":"string"},"description":"For TABLE-typed fields, the predefined keys each row may contain."},"validators":{"type":"array","nullable":true,"items":{"type":"object","properties":{"type":{"type":"string","enum":["NotEmpty","CharacterLength","Url","Email","Number","Range"]},"min":{"type":"number","nullable":true},"max":{"type":"number","nullable":true}},"required":["type"]},"description":"Server-enforced rules applied to this field at create and patch."}},"required":["id","type","title"]}}},"required":["id","name","category","fields"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-variables":{"get":{"operationId":"listTagManagerVariables","tags":["Tag Manager Variables"],"x-stainless-method":"tag_manager_variables.list","security":[{"apiKey":[]}],"description":"List variables inside a single tag manager. Requires the `tagManagerId` query parameter — variables are always scoped to one parent container. Supports cursor pagination via `limit` and `cursor`; the limit clamp is 1000 so a single request can return the full set (the web-app workspace renders all variables in one shot). Requires scope: tagManagers:find","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of variables to return. Defaults to 25; values below 1 are clamped to 1 and values above 1000 are clamped to 1000. The web-app passes 1000 to render the full workspace in one request.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Parent tag manager whose variables should be returned."},"required":true,"name":"tagManagerId","in":"query"}],"responses":{"200":{"description":"Variables scoped to a parent tag manager","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Variable type discriminator. Examples that exist today: `DataLayer`, `Constant`, `Cookie`, `Url`, `UrlParameter`, `Weekday`, `RandomNumber`. Pick from `GET /tag-manager-variables/types` for the canonical set."},"parameters":{"type":"object","description":"Type-specific configuration.","additionalProperties":true},"defaultValue":{"type":"object","description":"Default value returned when no rule matches. JSON value — type depends on `type`.","additionalProperties":true},"lookUpTable":{"type":"object","description":"Optional lookup table for `LookUpTable`-style variables. JSON value.","additionalProperties":true},"enabled":{"type":"boolean","nullable":true},"folderId":{"type":"string","description":"Folder this variable belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createTagManagerVariables","tags":["Tag Manager Variables"],"x-stainless-method":"tag_manager_variables.create","security":[{"apiKey":[]}],"description":"Create a new variable inside a tag manager. `tagManagerId` is required in the body. Known input failures (e.g. duplicate variable name within the tag manager) are returned as HTTP 409 with the reason in the response `error` field. Requires scope: tagManagers:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tagManagerId":{"type":"string","minLength":1,"description":"Parent tag manager that will own the new variable."},"name":{"type":"string"},"type":{"type":"string","description":"Variable type discriminator. Pick from `GET /tag-manager-variables/types` for the canonical set (e.g. `DataLayer`, `Constant`, `Cookie`, `Url`)."},"parameters":{"type":"object","description":"Type-specific JSON configuration.","additionalProperties":true},"defaultValue":{"type":"object","description":"Optional default value. JSON value of any type.","additionalProperties":true,"nullable":true},"lookUpTable":{"type":"object","description":"Optional lookup table for `LookUpTable` variables.","additionalProperties":true,"nullable":true},"enabled":{"type":"boolean","nullable":true}},"required":["tagManagerId","name","type","parameters"]}}}},"responses":{"200":{"description":"Created variable","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Variable type discriminator. Examples that exist today: `DataLayer`, `Constant`, `Cookie`, `Url`, `UrlParameter`, `Weekday`, `RandomNumber`. Pick from `GET /tag-manager-variables/types` for the canonical set."},"parameters":{"type":"object","description":"Type-specific configuration.","additionalProperties":true},"defaultValue":{"type":"object","description":"Default value returned when no rule matches. JSON value — type depends on `type`.","additionalProperties":true},"lookUpTable":{"type":"object","description":"Optional lookup table for `LookUpTable`-style variables. JSON value.","additionalProperties":true},"enabled":{"type":"boolean","nullable":true},"folderId":{"type":"string","description":"Folder this variable belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — variable name collides with another variable in the same tag manager, or another business-rule validation failed. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-variables/{id}":{"get":{"operationId":"findTagManagerVariables","tags":["Tag Manager Variables"],"x-stainless-method":"tag_manager_variables.retrieve","security":[{"apiKey":[]}],"description":"Find a single tag manager variable by ID. Requires scope: tagManagers:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Variable details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Variable type discriminator. Examples that exist today: `DataLayer`, `Constant`, `Cookie`, `Url`, `UrlParameter`, `Weekday`, `RandomNumber`. Pick from `GET /tag-manager-variables/types` for the canonical set."},"parameters":{"type":"object","description":"Type-specific configuration.","additionalProperties":true},"defaultValue":{"type":"object","description":"Default value returned when no rule matches. JSON value — type depends on `type`.","additionalProperties":true},"lookUpTable":{"type":"object","description":"Optional lookup table for `LookUpTable`-style variables. JSON value.","additionalProperties":true},"enabled":{"type":"boolean","nullable":true},"folderId":{"type":"string","description":"Folder this variable belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchTagManagerVariables","tags":["Tag Manager Variables"],"x-stainless-method":"tag_manager_variables.update","security":[{"apiKey":[]}],"description":"Partially update a variable. Only the fields you send are changed. Name collisions with other variables in the same tag manager return 409 with the reason in the response `error` field. To assign a variable to a folder, use `POST /rest/v1/tag-manager-asset-folders`. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Updated variable name."},"type":{"type":"string","description":"Updated variable type. Pick from `GET /tag-manager-variables/types`."},"parameters":{"type":"object","description":"Updated type-specific JSON configuration.","additionalProperties":true},"defaultValue":{"type":"object","description":"Updated default value. JSON value of any type.","additionalProperties":true,"nullable":true},"lookUpTable":{"type":"object","description":"Updated lookup table payload.","additionalProperties":true,"nullable":true},"enabled":{"type":"boolean","description":"Pause/resume the variable without changing other fields.","nullable":true}}}}}},"responses":{"200":{"description":"Updated variable","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","description":"Variable type discriminator. Examples that exist today: `DataLayer`, `Constant`, `Cookie`, `Url`, `UrlParameter`, `Weekday`, `RandomNumber`. Pick from `GET /tag-manager-variables/types` for the canonical set."},"parameters":{"type":"object","description":"Type-specific configuration.","additionalProperties":true},"defaultValue":{"type":"object","description":"Default value returned when no rule matches. JSON value — type depends on `type`.","additionalProperties":true},"lookUpTable":{"type":"object","description":"Optional lookup table for `LookUpTable`-style variables. JSON value.","additionalProperties":true},"enabled":{"type":"boolean","nullable":true},"folderId":{"type":"string","description":"Folder this variable belongs to. Settable via PATCH — send a folder UUID to assign, or `null` to remove from its current folder.","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name","type","parameters"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — name collision or other business-rule validation failure. Reason is in the response `error` field.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteTagManagerVariables","tags":["Tag Manager Variables"],"x-stainless-method":"tag_manager_variables.delete","security":[{"apiKey":[]}],"description":"Delete a tag manager variable. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-variables/types":{"get":{"operationId":"getTagManagerVariableTypes","tags":["Tag Manager Variables"],"x-stainless-method":"tag_manager_variables.types","security":[{"apiKey":[]}],"description":"Lists every variable template the platform supports — what `type` discriminator to send on create/patch, the shape of the type-specific `parameters` payload, and `supportsVariables` (whether the variable's own parameter fields may reference `{{OtherVariable}}` at runtime). Account-agnostic: the response is the same for every API key. Requires scope: tagManagers:find","responses":{"200":{"description":"Variable template descriptors","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Type discriminator — pass this as `type` on create/patch."},"name":{"type":"string","description":"Human-readable display name."},"category":{"type":"string","description":"Grouping label."},"description":{"type":"string","nullable":true},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Parameter key that goes in the `parameters` payload at create/patch."},"type":{"type":"string","enum":["STRING","BOOLEAN","INTEGER","FLOAT","TABLE"],"description":"Underlying data type of the parameter value."},"title":{"type":"string","description":"Human-readable title for the field."},"description":{"type":"string","nullable":true},"required":{"type":"boolean","nullable":true,"description":"When `true`, omitting or sending an empty value for this parameter on create/patch returns HTTP 400."},"default":{"type":"object","description":"Default value when the caller omits the parameter on create.","additionalProperties":true},"availableValues":{"type":"array","nullable":true,"items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"]},"description":"When present, the field accepts only one of these values. Send the `value` string in `parameters`; the `label` is for display."},"allowedKeys":{"type":"array","nullable":true,"items":{"type":"string"},"description":"For TABLE-typed fields, the predefined keys each row may contain."},"validators":{"type":"array","nullable":true,"items":{"type":"object","properties":{"type":{"type":"string","enum":["NotEmpty","CharacterLength","Url","Email","Number","Range"]},"min":{"type":"number","nullable":true},"max":{"type":"number","nullable":true}},"required":["type"]},"description":"Server-enforced rules applied to this field at create and patch."}},"required":["id","type","title"]}},"supportsVariables":{"type":"boolean","nullable":true,"description":"When `true`, this variable type's parameter fields can themselves contain `{{OtherVariable}}` references that the SDK resolves at runtime."}},"required":["id","name","category","fields"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-folders":{"get":{"operationId":"listTagManagerFolders","tags":["Tag Manager Folders"],"x-stainless-method":"tag_manager_folders.list","security":[{"apiKey":[]}],"description":"List folders inside a single tag manager. Folders are dashboard-only organizational containers — they do not affect tag evaluation. Requires the `tagManagerId` query parameter. Supports cursor pagination via `limit` and `cursor`; the limit clamp is 1000 so a single request can return the full set (the web-app workspace renders all folders in one shot). Requires scope: tagManagers:find","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of folders to return. Defaults to 25; values below 1 are clamped to 1 and values above 1000 are clamped to 1000. The web-app passes 1000 to render the full workspace in one request.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Parent tag manager whose folders should be returned."},"required":true,"name":"tagManagerId","in":"query"}],"responses":{"200":{"description":"Folders scoped to a parent tag manager","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createTagManagerFolders","tags":["Tag Manager Folders"],"x-stainless-method":"tag_manager_folders.create","security":[{"apiKey":[]}],"description":"Create a folder inside a tag manager. `tagManagerId` is required in the body. Names are case-insensitively unique within the tag manager — collisions return 409 with the reason in the response `error` field. Requires scope: tagManagers:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tagManagerId":{"type":"string","minLength":1,"description":"Parent tag manager that will own the new folder."},"name":{"type":"string","minLength":1,"description":"Folder name. Case-insensitively unique within the tag manager."}},"required":["tagManagerId","name"]}}}},"responses":{"200":{"description":"Created folder","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — a folder with the same name (case-insensitive) already exists in this tag manager.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-folders/{id}":{"get":{"operationId":"findTagManagerFolders","tags":["Tag Manager Folders"],"x-stainless-method":"tag_manager_folders.retrieve","security":[{"apiKey":[]}],"description":"Find a single tag manager folder by ID. Requires scope: tagManagers:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Folder details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchTagManagerFolders","tags":["Tag Manager Folders"],"x-stainless-method":"tag_manager_folders.update","security":[{"apiKey":[]}],"description":"Rename a folder. The new name must be case-insensitively unique within the tag manager; collisions return 409. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"New folder name. Case-insensitively unique within the tag manager."}},"required":["name"]}}}},"responses":{"200":{"description":"Updated folder","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"tagManagerId":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","tagManagerId","accountId","name"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — a folder with the same name (case-insensitive) already exists in this tag manager.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteTagManagerFolders","tags":["Tag Manager Folders"],"x-stainless-method":"tag_manager_folders.delete","security":[{"apiKey":[]}],"description":"Delete a folder. Tags, triggers, and variables previously assigned to the folder are no longer grouped under it; the assets themselves are not deleted. Requires scope: tagManagers:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/tag-manager-asset-folders":{"post":{"operationId":"createTagManagerAssetFolders","tags":["Tag Manager Asset Folders"],"x-stainless-method":"tag_manager_asset_folders.create","security":[{"apiKey":[]}],"description":"Assign a tag, trigger, or variable to a folder within its tag manager, or send `folderId: null` to remove the asset from its current folder. The assignment is a full replace — calling it again with a different `folderId` silently moves the asset. Requires scope: tagManagers:update","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assetType":{"type":"string","enum":["tagManagerTag","tagManagerTrigger","tagManagerVariable"],"description":"Asset type to assign. Must be one of `tagManagerTag`, `tagManagerTrigger`, or `tagManagerVariable`."},"assetId":{"type":"string","format":"uuid","description":"UUID of the tag, trigger, or variable to assign."},"folderId":{"type":"string","nullable":true,"format":"uuid","description":"Folder UUID to assign to. Send `null` to remove the asset from its current folder."}},"required":["assetType","assetId","folderId"]}}}},"responses":{"200":{"description":"Updated folder assignment","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"tagManagerId":{"type":"string"},"assetType":{"type":"string","enum":["tagManagerTag","tagManagerTrigger","tagManagerVariable"]},"assetId":{"type":"string"},"folderId":{"type":"string","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","tagManagerId","assetType","assetId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/attribution/initial":{"get":{"operationId":"getAttributionInitial","tags":["Attribution"],"x-stainless-method":"attribution.initial","security":[{"apiKey":[]}],"description":"Returns the top-15 values for each UTM dimension (source, medium, campaign, content, term, name) and referring domain attributed to the conversion event on a first-touch basis for the given date window. Use `from`/`to` to set the analysis window (max 60 days). Optionally filter to a specific UTM combo with `utmSource`, `utmMedium`, etc. The counts represent unique visitors who performed the specified `eventName` and were attributed to each UTM value. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-05-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-06-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Conversion event to count. Must be a selectable conversion event.","example":"purchase"},"required":true,"name":"eventName","in":"query"},{"schema":{"type":"string","enum":["INITIAL","LAST_TOUCH"],"description":"Attribution type for UTM filter matching. Defaults to `INITIAL`."},"required":false,"name":"attributionType","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM source."},"required":false,"name":"utmSource","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM medium."},"required":false,"name":"utmMedium","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM campaign."},"required":false,"name":"utmCampaign","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM content."},"required":false,"name":"utmContent","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM term."},"required":false,"name":"utmTerm","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM name."},"required":false,"name":"utmName","in":"query"}],"responses":{"200":{"description":"Initial (first-touch) attribution breakdown by UTM dimension and referring domain","content":{"application/json":{"schema":{"type":"object","properties":{"initial_utm_source":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"initial_utm_medium":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"initial_utm_campaign":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"initial_utm_content":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"initial_utm_term":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"initial_utm_name":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"initial_referring_domain":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}}},"required":["initial_utm_source","initial_utm_medium","initial_utm_campaign","initial_utm_content","initial_utm_term","initial_utm_name","initial_referring_domain"]}}}},"400":{"description":"Bad request — malformed `from`/`to` or window longer than 60 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/attribution/last-touch":{"get":{"operationId":"getAttributionLastTouch","tags":["Attribution"],"x-stainless-method":"attribution.lastTouch","security":[{"apiKey":[]}],"description":"Returns the top-15 values for each UTM dimension (source, medium, campaign, content, term, name) and referring domain attributed to the conversion event on a last-touch basis for the given date window. Use `from`/`to` to set the analysis window (max 60 days). The counts represent unique visitors who performed the specified `eventName` and were attributed to each UTM value on their most recent session. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-05-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-06-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Conversion event to count. Must be a selectable conversion event.","example":"purchase"},"required":true,"name":"eventName","in":"query"},{"schema":{"type":"string","enum":["INITIAL","LAST_TOUCH"],"description":"Attribution type for UTM filter matching. Defaults to `LAST_TOUCH`."},"required":false,"name":"attributionType","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM source."},"required":false,"name":"utmSource","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM medium."},"required":false,"name":"utmMedium","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM campaign."},"required":false,"name":"utmCampaign","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM content."},"required":false,"name":"utmContent","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM term."},"required":false,"name":"utmTerm","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Filter by UTM name."},"required":false,"name":"utmName","in":"query"}],"responses":{"200":{"description":"Last-touch attribution breakdown by UTM dimension and referring domain","content":{"application/json":{"schema":{"type":"object","properties":{"utm_source":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"utm_medium":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"utm_campaign":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"utm_content":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"utm_term":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"utm_name":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}},"referring_domain":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"count":{"type":"integer"}},"required":["value","count"]}}},"required":["utm_source","utm_medium","utm_campaign","utm_content","utm_term","utm_name","referring_domain"]}}}},"400":{"description":"Bad request — malformed `from`/`to` or window longer than 60 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/attribution/conversion":{"get":{"operationId":"getAttributionConversion","tags":["Attribution"],"x-stainless-method":"attribution.conversion","security":[{"apiKey":[]}],"description":"Multi-touch conversion attribution: returns a source → medium → campaign hierarchy with attributed converter credits distributed according to the selected `attributionModel`. Scoped to all web sources by default; optionally narrow to a single web source via `webSourceId`. Date range is capped at 31 days; lookback window is capped at 60 days. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 31 days or fewer.","example":"2026-06-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 31 days or fewer.","example":"2026-06-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Conversion event to attribute. Must be a selectable conversion event.","example":"purchase"},"required":true,"name":"eventName","in":"query"},{"schema":{"type":"string","enum":["FIRST_TOUCH","LAST_TOUCH","LINEAR","POSITION_BASED"],"description":"Attribution model to apply to multi-touch conversion paths."},"required":true,"name":"attributionModel","in":"query"},{"schema":{"type":"string","enum":["SEVEN_DAYS","FOURTEEN_DAYS","THIRTY_DAYS","SIXTY_DAYS","NINETY_DAYS","ONE_HUNDRED_EIGHTY_DAYS","UNLIMITED"],"description":"How far back before each conversion to consider touchpoints. Capped at 60 days for this report. Defaults to `THIRTY_DAYS`."},"required":false,"name":"lookbackWindow","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":5000,"description":"Maximum number of leaf-level attribution rows to return. Defaults to 1000."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Scope to a single web source by id, or omit for all sources (account-wide)."},"required":false,"name":"webSourceId","in":"query"}],"responses":{"200":{"description":"Multi-touch conversion attribution hierarchy","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"object","properties":{"totalEventConverters":{"type":"integer"},"scopeConverters":{"type":"integer"},"attributedConverters":{"type":"integer"},"totalSessions":{"type":"integer"}},"required":["totalEventConverters","scopeConverters","attributedConverters","totalSessions"]},"nodes":{"type":"array","items":{"type":"object","properties":{"level":{"type":"string","enum":["SOURCE","MEDIUM","CAMPAIGN"]},"source":{"type":"string"},"medium":{"type":"string","nullable":true},"campaign":{"type":"string","nullable":true},"attributedConverterCredit":{"type":"number"},"converters":{"type":"integer"},"sessions":{"type":"integer"}},"required":["level","source","attributedConverterCredit","converters","sessions"]}},"isTruncated":{"type":"boolean"},"totalLeafRows":{"type":"integer"},"maxLeafRows":{"type":"integer"}},"required":["summary","nodes","isTruncated","totalLeafRows","maxLeafRows"]}}}},"400":{"description":"Bad request — malformed `from`/`to`, window longer than 31 days, or invalid `eventName`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/attribution/audience-conversion":{"get":{"operationId":"getAttributionAudienceConversion","tags":["Attribution"],"x-stainless-method":"attribution.audienceConversion","security":[{"apiKey":[]}],"description":"Audience performance conversion report: returns a summary of converters and conversion rate for the selected event and date window, a per-day timeseries, and a UTM source/medium/campaign breakdown. Optionally compare against the preceding period of equal length when `attributionWindow` is `IN_RANGE`. Date range is capped at 60 days. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-05-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 60 days or fewer.","example":"2026-06-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Conversion event to analyze.","example":"purchase"},"required":true,"name":"eventName","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Event property to sum as conversion value.","example":"revenue"},"required":false,"name":"valueProperty","in":"query"},{"schema":{"type":"string","description":"Attribution window: `IN_RANGE` or a number of lookback days (e.g. `7`, `30`). Defaults to `IN_RANGE`.","example":"IN_RANGE"},"required":false,"name":"attributionWindow","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter results to a specific web source by its UUID.","example":"550e8400-e29b-41d4-a716-446655440000"},"required":false,"name":"webSourceId","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"Exclude bot sessions. Defaults to `true`.","example":"true"},"required":false,"name":"excludeBots","in":"query"}],"responses":{"200":{"description":"Audience conversion report with summary, timeseries, and UTM breakdown","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"object","properties":{"audienceSize":{"type":"integer"},"conversions":{"type":"integer"},"totalValue":{"type":"number"},"converters":{"type":"integer"},"conversionRate":{"type":"number"},"avgValuePerConversion":{"type":"number"},"avgValuePerConvertingVisitor":{"type":"number"}},"required":["audienceSize","conversions","totalValue","converters","conversionRate","avgValuePerConversion","avgValuePerConvertingVisitor"]},"previousSummary":{"type":"object","nullable":true,"properties":{"audienceSize":{"type":"integer"},"conversions":{"type":"integer"},"totalValue":{"type":"number"},"converters":{"type":"integer"},"conversionRate":{"type":"number"},"avgValuePerConversion":{"type":"number"},"avgValuePerConvertingVisitor":{"type":"number"}},"required":["audienceSize","conversions","totalValue","converters","conversionRate","avgValuePerConversion","avgValuePerConvertingVisitor"]},"timeseries":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"conversions":{"type":"integer"},"totalValue":{"type":"number"}},"required":["date","conversions","totalValue"]}},"breakdown":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"medium":{"type":"string"},"campaign":{"type":"string"},"conversions":{"type":"integer"},"converters":{"type":"integer"},"totalValue":{"type":"number"}},"required":["source","medium","campaign","conversions","converters","totalValue"]}}},"required":["summary","timeseries","breakdown"]}}}},"400":{"description":"Bad request — malformed `from`/`to` or window longer than 60 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/attribution/utm-comparison":{"get":{"operationId":"getAttributionUtmComparison","tags":["Attribution"],"x-stainless-method":"attribution.utmComparison","security":[{"apiKey":[]}],"description":"Compare up to 5 UTM dimension combinations side-by-side for a single conversion event. Each combo returns the unique visitors, sessions, total events, and derived conversion rate for that UTM filter within the window. Requires both `web-analytics:view` and `report:event-count-by-day` API-key scopes. Date range is capped at 31 days. Pass `combos` as a single JSON-encoded array: `combos=[{\"utmSource\":\"google\",\"utmMedium\":\"cpc\"},{\"utmSource\":\"meta\"}]`. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 31 days or fewer.","example":"2026-06-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window, as a UTC calendar day in `YYYY-MM-DD` format. The window between `from` and `to` must be 31 days or fewer.","example":"2026-06-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Conversion event to compare across UTM combos.","example":"purchase"},"required":true,"name":"eventName","in":"query"},{"schema":{"type":"string","description":"JSON-encoded array of UTM dimension combos to compare side-by-side (min 1, max 5). Each combo is an object with optional `utmSource`, `utmMedium`, `utmCampaign`, `utmContent`, `utmTerm` fields.","example":"[{\"utmSource\":\"google\",\"utmMedium\":\"cpc\"},{\"utmSource\":\"meta\"}]"},"required":true,"name":"combos","in":"query"}],"responses":{"200":{"description":"Side-by-side UTM performance comparison for up to 5 UTM combos","content":{"application/json":{"schema":{"type":"object","properties":{"combos":{"type":"array","items":{"type":"object","properties":{"combo":{"type":"object","properties":{"utmSource":{"type":"string","nullable":true},"utmMedium":{"type":"string","nullable":true},"utmCampaign":{"type":"string","nullable":true},"utmContent":{"type":"string","nullable":true},"utmTerm":{"type":"string","nullable":true}}},"visitors":{"type":"integer"},"sessions":{"type":"integer"},"events":{"type":"integer"},"conversionRate":{"type":"number"}},"required":["combo","visitors","sessions","events","conversionRate"]},"description":"Per-combo metrics in the same order as the input `combos` array."},"range":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"description":"The requested date range as returned by the server."},"eventName":{"type":"string","description":"The conversion event that was analyzed."}},"required":["combos","range","eventName"]}}}},"400":{"description":"Bad request — malformed `from`/`to`, window longer than 31 days, malformed `combos` JSON, or more than 5 combos.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-analytics/overview":{"get":{"operationId":"getWebAnalyticsOverview","tags":["Web Analytics"],"x-stainless-method":"web_analytics.overview","security":[{"apiKey":[]}],"description":"Return privacy-first traffic metrics and a timeseries for the requested date range. Filter by source, geography, page, campaign, device, or other supported dimensions with the JSON-encoded `filters` query parameter. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-31"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional web source UUID. Omit to aggregate all web sources in the account."},"required":false,"name":"webSourceId","in":"query"},{"schema":{"type":"boolean","description":"Exclude detected bot sessions. Defaults to true."},"required":false,"name":"excludeBots","in":"query"},{"schema":{"type":"string","description":"Optional JSON-encoded array of up to 20 filters. Dimensions: `page`, `entry_page`, `exit_page`, `source`, `medium`, `campaign`, `content`, `term`, `referrer`, `country`, `region`, `city`, `device`, `browser`, `os`. Each filter has a dimension, optional operator (`IS`, `IS_NOT`, `CONTAINS`, `NOT_CONTAINS`; defaults to `IS`), and one or more values. Example: `[{\"dimension\":\"country\",\"values\":[\"United States\"]}]`."},"required":false,"name":"filters","in":"query"},{"schema":{"type":"string","enum":["minute","day","week","month"],"description":"Timeseries bucket interval. Minute queries are capped to the most recent 24 hours."},"required":true,"name":"interval","in":"query"},{"schema":{"type":"string","enum":["unique_visitors","total_visits","pageviews","views_per_visit","bounce_rate","visit_duration"]},"required":true,"name":"metric","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Optional ISO timestamp used as the lower bound for realtime queries."},"required":false,"name":"realtimeFrom","in":"query"}],"responses":{"200":{"description":"Web traffic metrics and timeseries","content":{"application/json":{"schema":{"type":"object","properties":{"metrics":{"type":"object","nullable":true,"properties":{"uniqueVisitors":{"type":"integer"},"totalVisits":{"type":"integer"},"pageviews":{"type":"integer"},"viewsPerVisit":{"type":"number","description":"Average pageviews per visit."},"bounceRate":{"type":"number","description":"Percentage from 0 to 100 of visits with exactly one pageview."},"visitDuration":{"type":"number","description":"Average visit duration in seconds."}},"required":["uniqueVisitors","totalVisits","pageviews","viewsPerVisit","bounceRate","visitDuration"]},"timeseries":{"type":"array","nullable":true,"items":{"type":"object","properties":{"date":{"type":"string"},"value":{"type":"number"}},"required":["date","value"]}},"dataUpdatedAt":{"type":"string"}},"required":["metrics","timeseries","dataUpdatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-analytics/sources":{"get":{"operationId":"getWebAnalyticsSources","tags":["Web Analytics"],"x-stainless-method":"web_analytics.sources","security":[{"apiKey":[]}],"description":"Return visitor counts grouped by referrer or UTM source dimension for the requested date range. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-31"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional web source UUID. Omit to aggregate all web sources in the account."},"required":false,"name":"webSourceId","in":"query"},{"schema":{"type":"boolean","description":"Exclude detected bot sessions. Defaults to true."},"required":false,"name":"excludeBots","in":"query"},{"schema":{"type":"string","description":"Optional JSON-encoded array of up to 20 filters. Dimensions: `page`, `entry_page`, `exit_page`, `source`, `medium`, `campaign`, `content`, `term`, `referrer`, `country`, `region`, `city`, `device`, `browser`, `os`. Each filter has a dimension, optional operator (`IS`, `IS_NOT`, `CONTAINS`, `NOT_CONTAINS`; defaults to `IS`), and one or more values. Example: `[{\"dimension\":\"country\",\"values\":[\"United States\"]}]`."},"required":false,"name":"filters","in":"query"},{"schema":{"type":"string","enum":["referrer","campaign","source","medium","content","term"]},"required":true,"name":"dimension","in":"query"}],"responses":{"200":{"description":"Traffic source breakdown","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"visitors":{"type":"integer"}},"required":["name","visitors"]}},"totalCount":{"type":"integer"},"dataUpdatedAt":{"type":"string"}},"required":["rows","totalCount","dataUpdatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-analytics/pages":{"get":{"operationId":"getWebAnalyticsPages","tags":["Web Analytics"],"x-stainless-method":"web_analytics.pages","security":[{"apiKey":[]}],"description":"Return page-level traffic metrics for top pages, entry pages, or exit pages in the requested date range. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-31"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional web source UUID. Omit to aggregate all web sources in the account."},"required":false,"name":"webSourceId","in":"query"},{"schema":{"type":"boolean","description":"Exclude detected bot sessions. Defaults to true."},"required":false,"name":"excludeBots","in":"query"},{"schema":{"type":"string","description":"Optional JSON-encoded array of up to 20 filters. Dimensions: `page`, `entry_page`, `exit_page`, `source`, `medium`, `campaign`, `content`, `term`, `referrer`, `country`, `region`, `city`, `device`, `browser`, `os`. Each filter has a dimension, optional operator (`IS`, `IS_NOT`, `CONTAINS`, `NOT_CONTAINS`; defaults to `IS`), and one or more values. Example: `[{\"dimension\":\"country\",\"values\":[\"United States\"]}]`."},"required":false,"name":"filters","in":"query"},{"schema":{"type":"string","enum":["top","entry","exit"]},"required":true,"name":"view","in":"query"}],"responses":{"200":{"description":"Top, entry, or exit page breakdown","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"pageHostname":{"type":"string"},"pagePath":{"type":"string"},"pageviews":{"type":"integer","nullable":true},"bounceRate":{"type":"number","nullable":true,"description":"Percentage from 0 to 100 of visits to the page that bounced."},"timeOnPage":{"type":"number","nullable":true,"description":"Average time on page in seconds when available."},"entries":{"type":"integer","nullable":true},"visitors":{"type":"integer","nullable":true},"exits":{"type":"integer","nullable":true},"exitRate":{"type":"number","nullable":true,"description":"Percentage from 0 to 100 of pageviews that ended a visit."}},"required":["pageHostname","pagePath","pageviews","bounceRate","timeOnPage","entries","visitors","exits","exitRate"]}},"totalCount":{"type":"integer"},"dataUpdatedAt":{"type":"string"}},"required":["rows","totalCount","dataUpdatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-analytics/locations":{"get":{"operationId":"getWebAnalyticsLocations","tags":["Web Analytics"],"x-stainless-method":"web_analytics.locations","security":[{"apiKey":[]}],"description":"Return visitor counts grouped by country, region, or city for the requested date range. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-31"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional web source UUID. Omit to aggregate all web sources in the account."},"required":false,"name":"webSourceId","in":"query"},{"schema":{"type":"boolean","description":"Exclude detected bot sessions. Defaults to true."},"required":false,"name":"excludeBots","in":"query"},{"schema":{"type":"string","description":"Optional JSON-encoded array of up to 20 filters. Dimensions: `page`, `entry_page`, `exit_page`, `source`, `medium`, `campaign`, `content`, `term`, `referrer`, `country`, `region`, `city`, `device`, `browser`, `os`. Each filter has a dimension, optional operator (`IS`, `IS_NOT`, `CONTAINS`, `NOT_CONTAINS`; defaults to `IS`), and one or more values. Example: `[{\"dimension\":\"country\",\"values\":[\"United States\"]}]`."},"required":false,"name":"filters","in":"query"},{"schema":{"type":"string","enum":["country","region","city"]},"required":true,"name":"dimension","in":"query"}],"responses":{"200":{"description":"Visitor location breakdown","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"code":{"type":"string","nullable":true},"visitors":{"type":"integer"}},"required":["name","code","visitors"]}},"totalCount":{"type":"integer"},"dataUpdatedAt":{"type":"string"}},"required":["rows","totalCount","dataUpdatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-analytics/devices":{"get":{"operationId":"getWebAnalyticsDevices","tags":["Web Analytics"],"x-stainless-method":"web_analytics.devices","security":[{"apiKey":[]}],"description":"Return visitor counts grouped by device type, browser, or operating system for the requested date range. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-31"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional web source UUID. Omit to aggregate all web sources in the account."},"required":false,"name":"webSourceId","in":"query"},{"schema":{"type":"boolean","description":"Exclude detected bot sessions. Defaults to true."},"required":false,"name":"excludeBots","in":"query"},{"schema":{"type":"string","description":"Optional JSON-encoded array of up to 20 filters. Dimensions: `page`, `entry_page`, `exit_page`, `source`, `medium`, `campaign`, `content`, `term`, `referrer`, `country`, `region`, `city`, `device`, `browser`, `os`. Each filter has a dimension, optional operator (`IS`, `IS_NOT`, `CONTAINS`, `NOT_CONTAINS`; defaults to `IS`), and one or more values. Example: `[{\"dimension\":\"country\",\"values\":[\"United States\"]}]`."},"required":false,"name":"filters","in":"query"},{"schema":{"type":"string","enum":["device","browser","os"]},"required":true,"name":"dimension","in":"query"}],"responses":{"200":{"description":"Visitor device breakdown","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"visitors":{"type":"integer"}},"required":["name","visitors"]}},"totalCount":{"type":"integer"},"dataUpdatedAt":{"type":"string"}},"required":["rows","totalCount","dataUpdatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-analytics/current-visitors":{"get":{"operationId":"getWebAnalyticsCurrentVisitors","tags":["Web Analytics"],"x-stainless-method":"web_analytics.currentVisitors","security":[{"apiKey":[]}],"description":"Return the distinct visitors active in the most recent 15-minute window, optionally scoped to one web source. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"uuid","description":"Optional web source UUID. Omit to count visitors across all account web sources."},"required":false,"name":"webSourceId","in":"query"}],"responses":{"200":{"description":"Current visitor count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"}},"required":["count"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/web-analytics/journey":{"get":{"operationId":"getWebAnalyticsJourney","tags":["Web Analytics"],"x-stainless-method":"web_analytics.journey","security":[{"apiKey":[]}],"description":"Return the next or previous journey steps for a pinned path. The `path` and `filters` query parameters are JSON-encoded arrays. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analysis window as `YYYY-MM-DD`.","example":"2026-07-31"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional web source UUID. Omit to aggregate all web sources in the account."},"required":false,"name":"webSourceId","in":"query"},{"schema":{"type":"boolean","description":"Exclude detected bot sessions. Defaults to true."},"required":false,"name":"excludeBots","in":"query"},{"schema":{"type":"string","description":"Optional JSON-encoded array of up to 20 journey filters. Supports web analytics dimensions plus `event_name`, `ep_currency`, `ep_appointment_id`, `ep_appointment_status`, `ep_service_line`, `ep_provider_id`, `ep_location_id`, `ep_booking_channel`, `ep_revenue_type`, `ep_call_outcome`, and `ep_staff_id`. Each filter has a dimension, optional operator (`IS`, `IS_NOT`, `CONTAINS`, `NOT_CONTAINS`; defaults to `IS`), and one or more values."},"required":false,"name":"filters","in":"query"},{"schema":{"type":"string","description":"JSON-encoded ordered path of opaque journey step keys. Use an empty array to request first-column candidates."},"required":true,"name":"path","in":"query"},{"schema":{"type":"string","enum":["forward","reverse"]},"required":false,"name":"direction","in":"query"},{"schema":{"type":"string","enum":["PAGE","EVENT"]},"required":false,"name":"stepKind","in":"query"},{"schema":{"type":"string","maxLength":500},"required":false,"name":"search","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":1000},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Journey Explorer step distribution","content":{"application/json":{"schema":{"type":"object","properties":{"anchorSessions":{"type":"integer"},"steps":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"kind":{"type":"string","nullable":true,"enum":["PAGE","EVENT"]},"label":{"type":"string"},"isTerminal":{"type":"boolean"},"isOther":{"type":"boolean"},"sessions":{"type":"integer"}},"required":["key","kind","label","isTerminal","isOther","sessions"]}},"hasMore":{"type":"boolean"}},"required":["anchorSessions","steps","hasMore"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/session-replays":{"get":{"operationId":"listSessionReplays","tags":["Session Replays"],"x-stainless-method":"session_replays.list","security":[{"apiKey":[]}],"description":"List recorded sessions for a date range. Filter by event, page, visitor, UTM fields, or an explicit JSON-encoded session ID list. Use `pagination.nextCursor` to retrieve the next page. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the replay window as `YYYY-MM-DD`."},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the replay window as `YYYY-MM-DD`."},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":500},"required":false,"name":"eventName","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":500},"required":false,"name":"pathname","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"required":false,"name":"visitorId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512},"required":false,"name":"utmSource","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512},"required":false,"name":"utmMedium","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512},"required":false,"name":"utmCampaign","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512},"required":false,"name":"utmContent","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512},"required":false,"name":"utmTerm","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512},"required":false,"name":"utmName","in":"query"},{"schema":{"type":"string","description":"Optional JSON-encoded session ID array. Maximum 100 session IDs."},"required":false,"name":"sessionIds","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Maximum replay sessions to return. Defaults to the report default."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"Session replay list","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string"},"visitorId":{"type":"string"},"date":{"type":"string"},"duration":{"type":"integer","nullable":true},"eventCount":{"type":"integer","nullable":true},"pageCount":{"type":"integer","nullable":true},"startTime":{"type":"string","nullable":true}},"required":["sessionId","visitorId","date","duration","eventCount","pageCount","startTime"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["items","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/session-replays/overview":{"get":{"operationId":"getSessionReplayOverview","tags":["Session Replays"],"x-stainless-method":"session_replays.overview","security":[{"apiKey":[]}],"description":"Return the total number of replay-bearing sessions and a daily timeseries for the requested date range. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the replay window as `YYYY-MM-DD`."},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the replay window as `YYYY-MM-DD`."},"required":true,"name":"to","in":"query"}],"responses":{"200":{"description":"Session replay totals and daily timeseries","content":{"application/json":{"schema":{"type":"object","properties":{"totalReplays":{"type":"integer"},"timeseries":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"value":{"type":"integer"}},"required":["date","value"]}}},"required":["totalReplays","timeseries"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/translation-widgets/{id}/analytics":{"get":{"operationId":"getTranslationWidgetAnalytics","tags":["Translation Widgets"],"x-stainless-method":"translation_widgets.analytics","security":[{"apiKey":[]}],"description":"Return usage totals and language, host, and page breakdowns for one translation widget over the requested date range. Requires scope: report:translation-analytics","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analytics window as `YYYY-MM-DD`."},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analytics window as `YYYY-MM-DD`."},"required":true,"name":"to","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"description":"Maximum rows to return for each breakdown. Defaults to 50."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Translation widget usage analytics","content":{"application/json":{"schema":{"type":"object","properties":{"totalTranslations":{"type":"integer"},"uniqueUsers":{"type":"integer"},"languagesUsed":{"type":"integer"},"byLanguage":{"type":"array","items":{"type":"object","properties":{"languageCode":{"type":"string"},"translations":{"type":"integer"},"uniqueUsers":{"type":"integer"}},"required":["languageCode","translations","uniqueUsers"]}},"byHost":{"type":"array","items":{"type":"object","properties":{"host":{"type":"string"},"translations":{"type":"integer"},"uniqueUsers":{"type":"integer"}},"required":["host","translations","uniqueUsers"]}},"topPages":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"translations":{"type":"integer"},"uniqueUsers":{"type":"integer"},"byLanguage":{"type":"array","items":{"type":"object","properties":{"languageCode":{"type":"string"},"translations":{"type":"integer"},"uniqueUsers":{"type":"integer"}},"required":["languageCode","translations","uniqueUsers"]}}},"required":["url","translations","uniqueUsers","byLanguage"]}}},"required":["totalTranslations","uniqueUsers","languagesUsed","byLanguage","byHost","topPages"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/conversion-journey-summaries":{"get":{"operationId":"listConversionJourneySummaries","tags":["Conversion Journey Summaries"],"x-stainless-method":"conversion_journey_summaries.list","security":[{"apiKey":[]}],"description":"List saved Conversion Journey Summary configurations, most recently updated first. Supports cursor pagination. Each result contains the conversion event, analysis window, attribution window, filters, and bot/source settings needed to reopen the saved analysis. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"Paginated saved conversion journey summaries","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"summaryId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"windowDays":{"type":"integer"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string"},"dateTo":{"type":"string"},"webSourceId":{"type":"string","nullable":true},"excludeBots":{"type":"boolean","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["summaryId","name","eventName","windowDays","filters","dateFrom","dateTo","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createConversionJourneySummaries","tags":["Conversion Journey Summaries"],"x-stainless-method":"conversion_journey_summaries.create","security":[{"apiKey":[]}],"description":"Save a named Conversion Journey Summary configuration. Returns the full saved summary so callers can reopen the same analysis without a follow-up request. Each account can save up to 100 summaries. Requires scope: web-analytics:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"eventName":{"type":"string","minLength":1,"maxLength":200},"windowDays":{"type":"integer","minimum":1,"maximum":90},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"value":{"type":"string","minLength":1,"maxLength":500},"values":{"type":"array","items":{"type":"string","minLength":1,"maxLength":500},"minItems":1,"maxItems":50}},"required":["dimension"]},"maxItems":20},"dateFrom":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive start of the saved analysis window in `YYYY-MM-DD` format.","example":"2026-06-01"},"dateTo":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive end of the saved analysis window in `YYYY-MM-DD` format.","example":"2026-06-30"},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}},"required":["name","eventName","windowDays","dateFrom","dateTo"]}}}},"responses":{"200":{"description":"Created saved conversion journey summary","content":{"application/json":{"schema":{"type":"object","properties":{"summaryId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"windowDays":{"type":"integer"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string"},"dateTo":{"type":"string"},"webSourceId":{"type":"string","nullable":true},"excludeBots":{"type":"boolean","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["summaryId","name","eventName","windowDays","filters","dateFrom","dateTo","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — the account has reached its saved Conversion Journey Summary limit.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/conversion-journey-summaries/{id}":{"get":{"operationId":"findConversionJourneySummaries","tags":["Conversion Journey Summaries"],"x-stainless-method":"conversion_journey_summaries.retrieve","security":[{"apiKey":[]}],"description":"Fetch a saved Conversion Journey Summary by its id. Returns 404 when it does not exist. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Saved conversion journey summary","content":{"application/json":{"schema":{"type":"object","properties":{"summaryId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"windowDays":{"type":"integer"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string"},"dateTo":{"type":"string"},"webSourceId":{"type":"string","nullable":true},"excludeBots":{"type":"boolean","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["summaryId","name","eventName","windowDays","filters","dateFrom","dateTo","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Saved conversion journey summary not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchConversionJourneySummaries","tags":["Conversion Journey Summaries"],"x-stainless-method":"conversion_journey_summaries.update","security":[{"apiKey":[]}],"description":"Update one or more fields on a saved Conversion Journey Summary. Omitted fields remain unchanged. When provided, `filters` replaces the complete saved filter list. Send `null` for `webSourceId` or `excludeBots` to clear that optional setting. Requires scope: web-analytics:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"eventName":{"type":"string","minLength":1,"maxLength":200},"windowDays":{"type":"integer","minimum":1,"maximum":90},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"value":{"type":"string","minLength":1,"maxLength":500},"values":{"type":"array","items":{"type":"string","minLength":1,"maxLength":500},"minItems":1,"maxItems":50}},"required":["dimension"]},"maxItems":20},"dateFrom":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"dateTo":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}}}}}},"responses":{"200":{"description":"Updated saved conversion journey summary","content":{"application/json":{"schema":{"type":"object","properties":{"summaryId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"windowDays":{"type":"integer"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string"},"dateTo":{"type":"string"},"webSourceId":{"type":"string","nullable":true},"excludeBots":{"type":"boolean","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["summaryId","name","eventName","windowDays","filters","dateFrom","dateTo","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Saved conversion journey summary not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteConversionJourneySummaries","tags":["Conversion Journey Summaries"],"x-stainless-method":"conversion_journey_summaries.delete","security":[{"apiKey":[]}],"description":"Delete a saved Conversion Journey Summary. The underlying analytics data is unaffected. Requires scope: web-analytics:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted saved conversion journey summary","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Saved conversion journey summary not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/funnels":{"get":{"operationId":"listFunnels","tags":["Funnels"],"x-stainless-method":"funnels.list","security":[{"apiKey":[]}],"description":"List every funnel configured on this account. Each funnel includes its step configuration, funnel type, and conversion window. Funnel results are computed on demand, so `status` is always `READY` and `reportDateRange` is always `null`; both fields are retained for backward compatibility and should not be used to decide whether results are available. Requires scope: web-analytics:view","responses":{"200":{"description":"All funnels configured on this account","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"funnelId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"name":{"type":"string"},"eventName":{"type":"string"},"order":{"type":"integer"},"filters":{"type":"object","description":"Step-level event filters (JSON object)."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Step-level event logic."}},"required":["stepId","name","eventName","order"]}},"funnelType":{"type":"string","enum":["SESSION_BASED","VISITOR_BASED"]},"utmFilters":{"type":"object","description":"Legacy exact-match UTM filters. Do not combine with globalLogic; globalLogic takes precedence.","nullable":true},"globalLogic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Nested visitor logic for the entire funnel. When supplied, this replaces legacy UTM filters."},"conversionWindow":{"type":"object","nullable":true,"properties":{"value":{"type":"integer","minimum":1},"unit":{"type":"string","enum":["MINUTES","HOURS","DAYS"]}},"required":["value","unit"]},"countingMethod":{"type":"string","nullable":true,"enum":["UNIQUES","TOTALS","SESSIONS"]},"stepOrder":{"type":"string","nullable":true,"enum":["EXACT","ANY"]},"watched":{"type":"boolean","nullable":true},"status":{"type":"string","enum":["READY","PROCESSING"]},"reportDateRange":{"type":"object","nullable":true,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["funnelId","name","steps","funnelType","status","createdAt","updatedAt"]},"description":"All funnels configured on this account."}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createFunnels","tags":["Funnels"],"x-stainless-method":"funnels.create","security":[{"apiKey":[]}],"description":"Create a session-based funnel with 2 to 10 ordered event steps. Returns the complete saved configuration. Requires scope: web-analytics:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"eventName":{"type":"string","minLength":1,"maxLength":200},"order":{"type":"integer","minimum":0},"filters":{"type":"object","nullable":true},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}}}},"required":["name","eventName","order"]},"minItems":2,"maxItems":10},"funnelType":{"type":"string","enum":["SESSION_BASED"],"description":"Funnels are session-based. `SESSION_BASED` is the only supported value and is applied when omitted."},"utmFilters":{"type":"object","description":"Legacy exact-match UTM filters. Do not combine with globalLogic; globalLogic takes precedence.","nullable":true},"globalLogic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Nested visitor logic for the entire funnel. When supplied, this replaces legacy UTM filters."},"conversionWindow":{"type":"object","nullable":true},"countingMethod":{"type":"string","nullable":true},"stepOrder":{"type":"string","nullable":true},"watched":{"type":"boolean","nullable":true}},"required":["name","steps"]}}}},"responses":{"200":{"description":"Created funnel configuration","content":{"application/json":{"schema":{"type":"object","properties":{"funnelId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"name":{"type":"string"},"eventName":{"type":"string"},"order":{"type":"integer"},"filters":{"type":"object","description":"Step-level event filters (JSON object)."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Step-level event logic."}},"required":["stepId","name","eventName","order"]}},"funnelType":{"type":"string","enum":["SESSION_BASED","VISITOR_BASED"]},"utmFilters":{"type":"object","description":"Legacy exact-match UTM filters. Do not combine with globalLogic; globalLogic takes precedence.","nullable":true},"globalLogic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Nested visitor logic for the entire funnel. When supplied, this replaces legacy UTM filters."},"conversionWindow":{"type":"object","nullable":true,"properties":{"value":{"type":"integer","minimum":1},"unit":{"type":"string","enum":["MINUTES","HOURS","DAYS"]}},"required":["value","unit"]},"countingMethod":{"type":"string","nullable":true,"enum":["UNIQUES","TOTALS","SESSIONS"]},"stepOrder":{"type":"string","nullable":true,"enum":["EXACT","ANY"]},"watched":{"type":"boolean","nullable":true},"status":{"type":"string","enum":["READY","PROCESSING"]},"reportDateRange":{"type":"object","nullable":true,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["funnelId","name","steps","funnelType","status","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/funnels/{id}":{"get":{"operationId":"findFunnels","tags":["Funnels"],"x-stainless-method":"funnels.retrieve","security":[{"apiKey":[]}],"description":"Fetch a single funnel configuration by its id. Returns `404` when the funnel does not exist or belongs to a different account. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Funnel configuration","content":{"application/json":{"schema":{"type":"object","properties":{"funnelId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"name":{"type":"string"},"eventName":{"type":"string"},"order":{"type":"integer"},"filters":{"type":"object","description":"Step-level event filters (JSON object)."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Step-level event logic."}},"required":["stepId","name","eventName","order"]}},"funnelType":{"type":"string","enum":["SESSION_BASED","VISITOR_BASED"]},"utmFilters":{"type":"object","description":"Legacy exact-match UTM filters. Do not combine with globalLogic; globalLogic takes precedence.","nullable":true},"globalLogic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Nested visitor logic for the entire funnel. When supplied, this replaces legacy UTM filters."},"conversionWindow":{"type":"object","nullable":true,"properties":{"value":{"type":"integer","minimum":1},"unit":{"type":"string","enum":["MINUTES","HOURS","DAYS"]}},"required":["value","unit"]},"countingMethod":{"type":"string","nullable":true,"enum":["UNIQUES","TOTALS","SESSIONS"]},"stepOrder":{"type":"string","nullable":true,"enum":["EXACT","ANY"]},"watched":{"type":"boolean","nullable":true},"status":{"type":"string","enum":["READY","PROCESSING"]},"reportDateRange":{"type":"object","nullable":true,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["funnelId","name","steps","funnelType","status","createdAt","updatedAt"],"description":"Funnel configuration details."}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchFunnels","tags":["Funnels"],"x-stainless-method":"funnels.update","security":[{"apiKey":[]}],"description":"Update one or more Funnel fields. Omitted fields remain unchanged. Send `null` to clear an optional field. `globalLogic` and legacy `utmFilters` cannot be set together. Requires scope: web-analytics:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"eventName":{"type":"string","minLength":1,"maxLength":200},"order":{"type":"integer","minimum":0},"filters":{"type":"object","nullable":true},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}}}},"required":["name","eventName","order"]},"minItems":2,"maxItems":10},"funnelType":{"type":"string","enum":["SESSION_BASED"]},"utmFilters":{"type":"object","description":"Legacy exact-match UTM filters. Do not combine with globalLogic; globalLogic takes precedence.","nullable":true},"globalLogic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Nested visitor logic for the entire funnel. When supplied, this replaces legacy UTM filters."},"conversionWindow":{"type":"object","nullable":true},"countingMethod":{"type":"string","nullable":true},"stepOrder":{"type":"string","nullable":true},"watched":{"type":"boolean","nullable":true}}}}}},"responses":{"200":{"description":"Updated funnel configuration","content":{"application/json":{"schema":{"type":"object","properties":{"funnelId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"name":{"type":"string"},"eventName":{"type":"string"},"order":{"type":"integer"},"filters":{"type":"object","description":"Step-level event filters (JSON object)."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Step-level event logic."}},"required":["stepId","name","eventName","order"]}},"funnelType":{"type":"string","enum":["SESSION_BASED","VISITOR_BASED"]},"utmFilters":{"type":"object","description":"Legacy exact-match UTM filters. Do not combine with globalLogic; globalLogic takes precedence.","nullable":true},"globalLogic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Nested visitor logic for the entire funnel. When supplied, this replaces legacy UTM filters."},"conversionWindow":{"type":"object","nullable":true,"properties":{"value":{"type":"integer","minimum":1},"unit":{"type":"string","enum":["MINUTES","HOURS","DAYS"]}},"required":["value","unit"]},"countingMethod":{"type":"string","nullable":true,"enum":["UNIQUES","TOTALS","SESSIONS"]},"stepOrder":{"type":"string","nullable":true,"enum":["EXACT","ANY"]},"watched":{"type":"boolean","nullable":true},"status":{"type":"string","enum":["READY","PROCESSING"]},"reportDateRange":{"type":"object","nullable":true,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["funnelId","name","steps","funnelType","status","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Funnel not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteFunnels","tags":["Funnels"],"x-stainless-method":"funnels.delete","security":[{"apiKey":[]}],"description":"Delete a Funnel configuration. Existing analytics data is unaffected. Requires scope: web-analytics:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted funnel","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Funnel not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/funnels/{id}/duplicate":{"post":{"operationId":"duplicateFunnels","tags":["Funnels"],"x-stainless-method":"funnels.duplicate","security":[{"apiKey":[]}],"description":"Duplicate a funnel configuration in the same account. The copy keeps the funnel steps and settings, receives a new ID, and is named `Copy of …`. Requires scope: web-analytics:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Duplicated funnel configuration","content":{"application/json":{"schema":{"type":"object","properties":{"funnelId":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string"},"name":{"type":"string"},"eventName":{"type":"string"},"order":{"type":"integer"},"filters":{"type":"object","description":"Step-level event filters (JSON object)."},"logic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Step-level event logic."}},"required":["stepId","name","eventName","order"]}},"funnelType":{"type":"string","enum":["SESSION_BASED","VISITOR_BASED"]},"utmFilters":{"type":"object","description":"Legacy exact-match UTM filters. Do not combine with globalLogic; globalLogic takes precedence.","nullable":true},"globalLogic":{"type":"object","nullable":true,"properties":{"AND":{"type":"array","nullable":true,"items":{"type":"object"},"description":"All child nodes must match. Each child is itself a logic node (leaf `condition` or combinator)."},"OR":{"type":"array","nullable":true,"items":{"type":"object"},"description":"Any child node must match. Each child is itself a logic node (leaf `condition` or combinator)."},"NOT":{"type":"object","description":"Negates a single child logic node."},"condition":{"type":"object","nullable":true,"properties":{"property":{"type":"string","description":"Bare dotted path into the event/visitor record. Examples: `$event.event`, `$event.event_properties.value`, `visitor.consent.marketing`. The leading `$` is optional and stripped before lookup. Do **not** use `{{...}}` here — that template syntax is for mapping values (`mappings[].map`), not logic conditions, and would be compared as a literal string."},"operator":{"type":"string","enum":["Is","IsNot","Contains","DoesNotContain","StartsWith","EndsWith","IsFalsy","IsTruthy","IsNull","IsNotNull","IsUndefined","IsNotUndefined","IsGreaterThan","IsGreaterThanOrEqual","IsLessThan","IsLessThanOrEqual","IsIn","IsNotIn","IsFoundIn","IsNotFoundIn","IsTrue","IsFalse","IsBefore","IsAfter","IsBetween","IsOnOrBefore","IsOnOrAfter","MatchesRegex","MatchesRegexIgnoreCase","DoesNotMatchRegex","DoesNotMatchRegexIgnoreCase"],"description":"Comparison verb in PascalCase. Equality/text: `Is`, `IsNot`, `Contains`, `DoesNotContain`, `StartsWith`, `EndsWith`. Truthiness/nullability: `IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`. Numeric: `IsGreaterThan`, `IsGreaterThanOrEqual`, `IsLessThan`, `IsLessThanOrEqual`. Set membership: `IsIn`, `IsNotIn`, `IsFoundIn`, `IsNotFoundIn`. Date: `IsBefore`, `IsAfter`, `IsBetween`, `IsOnOrBefore`, `IsOnOrAfter`. Regex: `MatchesRegex`, `MatchesRegexIgnoreCase`, `DoesNotMatchRegex`, `DoesNotMatchRegexIgnoreCase`."},"value":{"type":"string","description":"String compared against the resolved property. Operators that take no value (`IsFalsy`, `IsTruthy`, `IsNull`, `IsNotNull`, `IsUndefined`, `IsNotUndefined`, `IsTrue`, `IsFalse`) ignore this field — send `\"\"`."}},"required":["property","operator","value"]}},"description":"Nested visitor logic for the entire funnel. When supplied, this replaces legacy UTM filters."},"conversionWindow":{"type":"object","nullable":true,"properties":{"value":{"type":"integer","minimum":1},"unit":{"type":"string","enum":["MINUTES","HOURS","DAYS"]}},"required":["value","unit"]},"countingMethod":{"type":"string","nullable":true,"enum":["UNIQUES","TOTALS","SESSIONS"]},"stepOrder":{"type":"string","nullable":true,"enum":["EXACT","ANY"]},"watched":{"type":"boolean","nullable":true},"status":{"type":"string","enum":["READY","PROCESSING"]},"reportDateRange":{"type":"object","nullable":true,"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["funnelId","name","steps","funnelType","status","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Funnel not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/funnels/{id}/results":{"get":{"operationId":"getFunnelResults","tags":["Funnels"],"x-stainless-method":"funnels.results","security":[{"apiKey":[]}],"description":"Compute funnel step analytics for a funnel over a date window. Returns per-step visitor counts, conversion rates, drop-off rates, average time to next step, and sample session IDs for replay. Results are computed on demand from event data at request time, so any date window within the supported range returns current results. `to` must be on or after `from`, and the window may span at most 31 days including both endpoints. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the analysis window, as a UTC calendar day in `YYYY-MM-DD` format. The window may span at most 31 days including both endpoints.","example":"2026-06-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the analysis window, as a UTC calendar day in `YYYY-MM-DD` format. Must be on or after `from`, and the window may span at most 31 days including both endpoints.","example":"2026-06-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","enum":["DESKTOP","MOBILE","ALL"],"default":"ALL","description":"Restrict the funnel to sessions on a device class. `MOBILE` matches phone sessions; `DESKTOP` matches every session that is not a phone, tablet, TV, console, wearable, XR, or embedded device. `ALL` (the default) applies no device filter."},"required":false,"name":"deviceType","in":"query"},{"schema":{"type":"string","enum":["INITIAL","LAST_TOUCH"],"description":"Accepted for backward compatibility but NOT applied. Funnel sessions carry a single attribution set, so there is no initial vs. last-touch distinction to select between."},"required":false,"name":"attributionType","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Restrict the funnel to sessions whose `utm_source` exactly matches this value.","example":"google"},"required":false,"name":"utmSource","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Restrict the funnel to sessions whose `utm_medium` exactly matches this value.","example":"cpc"},"required":false,"name":"utmMedium","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Restrict the funnel to sessions whose `utm_campaign` exactly matches this value.","example":"spring-promo"},"required":false,"name":"utmCampaign","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Restrict the funnel to sessions whose `utm_content` exactly matches this value."},"required":false,"name":"utmContent","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Restrict the funnel to sessions whose `utm_term` exactly matches this value."},"required":false,"name":"utmTerm","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Accepted for backward compatibility but NOT applied — there is no campaign-name dimension on funnel sessions. Use `utmCampaign` instead."},"required":false,"name":"utmName","in":"query"}],"responses":{"200":{"description":"Funnel step analytics for the requested date window","content":{"application/json":{"schema":{"type":"object","properties":{"steps":{"type":"array","items":{"type":"object","properties":{"stepNumber":{"type":"integer"},"visitorCount":{"type":"integer"},"conversionCount":{"type":"integer"},"conversionRate":{"type":"number"},"dropOffRate":{"type":"number"},"overallConversionRate":{"type":"number"},"avgTimeToNextStep":{"type":"number","nullable":true},"sessionIds":{"type":"array","items":{"type":"string"}},"dropOffSessionIds":{"type":"array","items":{"type":"string"}}},"required":["stepNumber","visitorCount","conversionCount","conversionRate","dropOffRate","overallConversionRate","sessionIds","dropOffSessionIds"]},"description":"Per-step funnel analytics, ordered by step number."},"totalVisitors":{"type":"integer","description":"Total number of visitors who entered the funnel (entered step 1)."},"overallConversionRate":{"type":"number","description":"Conversion rate from first step to last step as a percentage."},"overallAvgTimeToConversion":{"type":"number","description":"Average time from first step to last step in seconds. Null when no completions.","nullable":true},"warning":{"type":"string","description":"Present when the results are wider than the funnel as configured. Some step conditions could not be expressed as a query and were ignored, so the counts above include visitors those conditions would have excluded. Absent when the whole definition was applied. The dashboard surfaces the same caveat.","example":"Ignored 1 unusable step condition."}},"required":["steps","totalVisitors","overallConversionRate"]}}}},"400":{"description":"Bad request — malformed `from`/`to` dates, `to` earlier than `from`, or a window longer than 31 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Funnel not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/audience-conversion-reports":{"get":{"operationId":"listAudiencePerformance","tags":["Audience Performance"],"x-stainless-method":"audience_conversion_reports.list","security":[{"apiKey":[]}],"description":"List saved Audience Performance report configurations, most recently updated first. Requires scope: web-analytics:view","responses":{"200":{"description":"Saved Audience Performance reports","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"reportId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"valueProperty":{"type":"string"},"attributionWindow":{"type":"string"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string","nullable":true},"dateTo":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}},"required":["reportId","name","eventName","valueProperty","attributionWindow","filters","createdAt","updatedAt"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createAudiencePerformance","tags":["Audience Performance"],"x-stainless-method":"audience_conversion_reports.create","security":[{"apiKey":[]}],"description":"Save an Audience Performance report configuration. Returns the full report so callers can run or update it without another request. Requires scope: web-analytics:write","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"eventName":{"type":"string","minLength":1,"maxLength":500},"valueProperty":{"type":"string","minLength":1,"maxLength":200},"attributionWindow":{"type":"string","pattern":"^(IN_RANGE|[1-9]|[12][0-9]|30)$"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"value":{"type":"string","minLength":1,"maxLength":500},"values":{"type":"array","items":{"type":"string","minLength":1,"maxLength":500},"minItems":1,"maxItems":50}},"required":["dimension"]},"maxItems":20},"dateFrom":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","nullable":true},"dateTo":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","nullable":true},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}},"required":["name","eventName","valueProperty","attributionWindow"]}}}},"responses":{"200":{"description":"Created Audience Performance report","content":{"application/json":{"schema":{"type":"object","properties":{"reportId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"valueProperty":{"type":"string"},"attributionWindow":{"type":"string"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string","nullable":true},"dateTo":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}},"required":["reportId","name","eventName","valueProperty","attributionWindow","filters","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The saved Audience Performance report limit has been reached.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/audience-conversion-reports/{id}":{"get":{"operationId":"findAudiencePerformance","tags":["Audience Performance"],"x-stainless-method":"audience_conversion_reports.retrieve","security":[{"apiKey":[]}],"description":"Fetch a saved Audience Performance report by id. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Saved Audience Performance report","content":{"application/json":{"schema":{"type":"object","properties":{"reportId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"valueProperty":{"type":"string"},"attributionWindow":{"type":"string"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string","nullable":true},"dateTo":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}},"required":["reportId","name","eventName","valueProperty","attributionWindow","filters","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Audience Performance report not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchAudiencePerformance","tags":["Audience Performance"],"x-stainless-method":"audience_conversion_reports.update","security":[{"apiKey":[]}],"description":"Update a saved Audience Performance report. Omitted fields remain unchanged, `filters: []` clears all filters, and dates must be sent or cleared as a pair. Requires scope: web-analytics:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"eventName":{"type":"string","minLength":1,"maxLength":500},"valueProperty":{"type":"string","minLength":1,"maxLength":200},"attributionWindow":{"type":"string","pattern":"^(IN_RANGE|[1-9]|[12][0-9]|30)$"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"value":{"type":"string","minLength":1,"maxLength":500},"values":{"type":"array","items":{"type":"string","minLength":1,"maxLength":500},"minItems":1,"maxItems":50}},"required":["dimension"]},"maxItems":20},"dateFrom":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","nullable":true},"dateTo":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","nullable":true},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}}}}}},"responses":{"200":{"description":"Updated Audience Performance report","content":{"application/json":{"schema":{"type":"object","properties":{"reportId":{"type":"string","format":"uuid"},"name":{"type":"string"},"eventName":{"type":"string"},"valueProperty":{"type":"string"},"attributionWindow":{"type":"string"},"filters":{"type":"array","items":{"type":"object","properties":{"dimension":{"type":"string","enum":["browser","campaign","city","content","country","device","entry_page","exit_page","medium","os","page","referrer","region","source","term"]},"operator":{"type":"string","enum":["CONTAINS","IS","IS_NOT","NOT_CONTAINS"]},"values":{"type":"array","items":{"type":"string"}}},"required":["dimension","operator","values"]}},"dateFrom":{"type":"string","nullable":true},"dateTo":{"type":"string","nullable":true},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"webSourceId":{"type":"string","format":"uuid","nullable":true},"excludeBots":{"type":"boolean","nullable":true}},"required":["reportId","name","eventName","valueProperty","attributionWindow","filters","createdAt","updatedAt"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Audience Performance report not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteAudiencePerformance","tags":["Audience Performance"],"x-stainless-method":"audience_conversion_reports.delete","security":[{"apiKey":[]}],"description":"Delete a saved Audience Performance report configuration. Requires scope: web-analytics:write","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted Audience Performance report","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Audience Performance report not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/audience-conversion-reports/{id}/results":{"get":{"operationId":"getAudienceConversionReportResults","tags":["Audience Performance"],"x-stainless-method":"audience_conversion_reports.results","security":[{"apiKey":[]}],"description":"Run a saved Audience Performance report. `from` and `to` override the saved date range when provided together. Returns 400 when neither a saved range nor an override is available. Requires scope: web-analytics:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Audience Performance report results","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"object","properties":{"audienceSize":{"type":"integer"},"conversions":{"type":"integer"},"totalValue":{"type":"number"},"converters":{"type":"integer"},"conversionRate":{"type":"number"},"avgValuePerConversion":{"type":"number"},"avgValuePerConvertingVisitor":{"type":"number"}},"required":["audienceSize","conversions","totalValue","converters","conversionRate","avgValuePerConversion","avgValuePerConvertingVisitor"]},"previousSummary":{"type":"object","nullable":true,"properties":{"audienceSize":{"type":"integer"},"conversions":{"type":"integer"},"totalValue":{"type":"number"},"converters":{"type":"integer"},"conversionRate":{"type":"number"},"avgValuePerConversion":{"type":"number"},"avgValuePerConvertingVisitor":{"type":"number"}},"required":["audienceSize","conversions","totalValue","converters","conversionRate","avgValuePerConversion","avgValuePerConvertingVisitor"]},"timeseries":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"conversions":{"type":"integer"},"totalValue":{"type":"number"}},"required":["date","conversions","totalValue"]}},"breakdown":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"medium":{"type":"string"},"campaign":{"type":"string"},"conversions":{"type":"integer"},"converters":{"type":"integer"},"totalValue":{"type":"number"}},"required":["source","medium","campaign","conversions","converters","totalValue"]}}},"required":["summary","timeseries","breakdown"]}}}},"400":{"description":"A complete date range is required.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Audience Performance report not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/short-links":{"get":{"operationId":"listShortLinks","tags":["Short Links"],"x-stainless-method":"short_links.list","security":[{"apiKey":[]}],"description":"List all short links (QR codes / redirects) for this account, newest first. Supports cursor pagination and optional `status` and `nameContains` filters. Each entity bundles the destination URL, immutable code, path format, and QR/campaign design. Requires scope: source:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["Disabled","Enabled"],"description":"Filter by short link status."},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Case-insensitive substring filter on the short link name."},"required":false,"name":"nameContains","in":"query"}],"responses":{"200":{"description":"Paginated list of short links","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this short link."},"isPublished":{"type":"boolean","description":"Whether this short link exists in the currently published version. An unpublished short link does not resolve at the edge.","nullable":true},"redirectUrl":{"type":"string","description":"The destination URL this short link redirects to.","nullable":true},"pixel":{"type":"string","description":"The immutable short code embedded in the public URL (`/r/{pixel}` for new links). Server-assigned.","nullable":true},"shortLinkCode":{"type":"string","description":"The public code embedded in the short-link URL.","nullable":true},"hasCustomShortLinkCode":{"type":"boolean","description":"Whether a user selected this code instead of using a generated code.","nullable":true},"shortUrl":{"type":"string","description":"The public short-link URL that the QR encodes and callers share. New links use `/r/{pixel}`; tracked event and campaign defaults are stored server-side. Also resolves on branded custom domains configured for the account.","nullable":true},"shortLinkDesign":{"type":"object","description":"QR styling + campaign tags. Null until the link is styled.","nullable":true}},"required":["id","status","createdAt","accountId"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createShortLinks","tags":["Short Links"],"x-stainless-method":"short_links.create","security":[{"apiKey":[]}],"description":"Create a short link (QR code / redirect) with its destination, campaign tags, and QR styling in a single call. The server atomically reserves an immutable compact code; `shortUrl` resolves as `/r/{pixel}` without tracking query parameters. All body fields are optional: send `{}` to create an unconfigured link and fill it in later with PATCH. A newly created short link only resolves at the edge once a version is published. Requires scope: source:create","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable name. Also sent as the tracked event name on every click/scan.","nullable":true,"example":"Spring Sale QR"},"redirectUrl":{"type":"string","description":"Destination URL the short link redirects to. Must be a valid URL.","nullable":true,"example":"https://example.com/spring"},"utm":{"type":"object","description":"Campaign / UTM tags appended to the tracked short-link URL.","nullable":true},"qr":{"type":"object","description":"QR code visual styling.","nullable":true},"code":{"type":"string","description":"Optional custom code in the shared public short-link namespace.","nullable":true}}}}}},"responses":{"200":{"description":"Created short link","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this short link."},"isPublished":{"type":"boolean","description":"Whether this short link exists in the currently published version. An unpublished short link does not resolve at the edge.","nullable":true},"redirectUrl":{"type":"string","description":"The destination URL this short link redirects to.","nullable":true},"pixel":{"type":"string","description":"The immutable short code embedded in the public URL (`/r/{pixel}` for new links). Server-assigned.","nullable":true},"shortLinkCode":{"type":"string","description":"The public code embedded in the short-link URL.","nullable":true},"hasCustomShortLinkCode":{"type":"boolean","description":"Whether a user selected this code instead of using a generated code.","nullable":true},"shortUrl":{"type":"string","description":"The public short-link URL that the QR encodes and callers share. New links use `/r/{pixel}`; tracked event and campaign defaults are stored server-side. Also resolves on branded custom domains configured for the account.","nullable":true},"shortLinkDesign":{"type":"object","description":"QR styling + campaign tags. Null until the link is styled.","nullable":true}},"required":["id","status","createdAt","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/short-links/{id}":{"get":{"operationId":"findShortLinks","tags":["Short Links"],"x-stainless-method":"short_links.retrieve","security":[{"apiKey":[]}],"description":"Fetch a single short link by id, including its destination, immutable code, composed `shortUrl`, and QR/campaign design. Returns 404 when no short link matches the id or it belongs to a different account. Requires scope: source:view","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Short link details","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this short link."},"isPublished":{"type":"boolean","description":"Whether this short link exists in the currently published version. An unpublished short link does not resolve at the edge.","nullable":true},"redirectUrl":{"type":"string","description":"The destination URL this short link redirects to.","nullable":true},"pixel":{"type":"string","description":"The immutable short code embedded in the public URL (`/r/{pixel}` for new links). Server-assigned.","nullable":true},"shortLinkCode":{"type":"string","description":"The public code embedded in the short-link URL.","nullable":true},"hasCustomShortLinkCode":{"type":"boolean","description":"Whether a user selected this code instead of using a generated code.","nullable":true},"shortUrl":{"type":"string","description":"The public short-link URL that the QR encodes and callers share. New links use `/r/{pixel}`; tracked event and campaign defaults are stored server-side. Also resolves on branded custom domains configured for the account.","nullable":true},"shortLinkDesign":{"type":"object","description":"QR styling + campaign tags. Null until the link is styled.","nullable":true}},"required":["id","status","createdAt","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchShortLinks","tags":["Short Links"],"x-stainless-method":"short_links.update","security":[{"apiKey":[]}],"description":"Partially update a short link. Only the fields you send are changed; omitted fields are unchanged. Send explicit `null` to clear `redirectUrl`. The `utm` and `qr` objects are replaced wholesale when sent. Returns the full short link entity after the update. Requires scope: source:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","nullable":true},"redirectUrl":{"type":"string","description":"Destination URL the short link redirects to. Must be a valid URL. Send `null` to clear it.","nullable":true},"status":{"type":"string","description":"Whether the short link resolves at the edge. Send `Enabled` or `Disabled`; `null` is rejected since storage cannot represent it.","nullable":true},"utm":{"type":"object","nullable":true},"qr":{"type":"object","nullable":true},"code":{"type":"string","nullable":true}}}}}},"responses":{"200":{"description":"Updated short link","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","nullable":true},"status":{"type":"string","enum":["Disabled","Enabled"]},"createdAt":{"type":"string"},"accountId":{"type":"string","description":"Organization id that owns this short link."},"isPublished":{"type":"boolean","description":"Whether this short link exists in the currently published version. An unpublished short link does not resolve at the edge.","nullable":true},"redirectUrl":{"type":"string","description":"The destination URL this short link redirects to.","nullable":true},"pixel":{"type":"string","description":"The immutable short code embedded in the public URL (`/r/{pixel}` for new links). Server-assigned.","nullable":true},"shortLinkCode":{"type":"string","description":"The public code embedded in the short-link URL.","nullable":true},"hasCustomShortLinkCode":{"type":"boolean","description":"Whether a user selected this code instead of using a generated code.","nullable":true},"shortUrl":{"type":"string","description":"The public short-link URL that the QR encodes and callers share. New links use `/r/{pixel}`; tracked event and campaign defaults are stored server-side. Also resolves on branded custom domains configured for the account.","nullable":true},"shortLinkDesign":{"type":"object","description":"QR styling + campaign tags. Null until the link is styled.","nullable":true}},"required":["id","status","createdAt","accountId"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteShortLinks","tags":["Short Links"],"x-stainless-method":"short_links.delete","security":[{"apiKey":[]}],"description":"Delete a short link and its QR/campaign design. After deletion the short URL stops resolving on the next publish. Requires scope: source:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","enum":[true]}},"required":["deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/short-links/{id}/results":{"get":{"operationId":"getShortLinkResults","tags":["Short Links"],"x-stainless-method":"short_links.results","security":[{"apiKey":[]}],"description":"Aggregate click analytics for a short link over a date window: total and unique clicks, a time series (daily or hourly), and breakdowns by country, city, and device. QR scans are counted as clicks. Pass `from`/`to` as UTC calendar days (`YYYY-MM-DD`); set `granularity=HOURLY` for hourly buckets and `excludeBots=false` to include bot traffic. Requires the `shortlink:reporting` scope, which is gated separately because analytics data is PHI-bearing. Requires scope: shortlink:reporting","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive lower bound of the report window, a UTC calendar day in `YYYY-MM-DD`.","example":"2026-06-01"},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive upper bound of the report window, a UTC calendar day in `YYYY-MM-DD`.","example":"2026-06-30"},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","enum":["DAILY","HOURLY"],"description":"Time-series bucket size. Defaults to `DAILY`."},"required":false,"name":"granularity","in":"query"},{"schema":{"type":"boolean","description":"Exclude bot traffic from the counts. Defaults to `true`."},"required":false,"name":"excludeBots","in":"query"}],"responses":{"200":{"description":"Click / scan analytics for the short link","content":{"application/json":{"schema":{"type":"object","properties":{"totalClicks":{"type":"number"},"uniqueClicks":{"type":"number"},"timeSeries":{"type":"array","items":{"type":"object","properties":{"period":{"type":"string"},"clicks":{"type":"number"},"uniqueClicks":{"type":"number"}},"required":["period","clicks","uniqueClicks"]}},"geoByCountry":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"clicks":{"type":"number"},"uniqueClicks":{"type":"number"}},"required":["name","clicks","uniqueClicks"]}},"geoByCity":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"clicks":{"type":"number"},"uniqueClicks":{"type":"number"}},"required":["name","clicks","uniqueClicks"]}},"devices":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"clicks":{"type":"number"},"uniqueClicks":{"type":"number"}},"required":["name","clicks","uniqueClicks"]}}},"required":["totalClicks","uniqueClicks","timeSeries","geoByCountry","geoByCity","devices"]}}}},"400":{"description":"Bad request — malformed `from`/`to` dates.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Short link not found.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/videos":{"get":{"operationId":"listVideos","tags":["Videos"],"x-stainless-method":"videos.list","security":[{"apiKey":[]}],"description":"List videos for the account, newest first. Supports cursor pagination and an optional case-insensitive title filter. Requires scope: media:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of items to return. Defaults to 25; values below 1 are clamped to 1 and values above 100 are clamped to 100.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","description":"Case-insensitive substring match on the video title."},"required":false,"name":"nameContains","in":"query"}],"responses":{"200":{"description":"Paginated list of videos","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"type":{"type":"string","enum":["Video"]},"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"hasVideoUpload":{"type":"boolean","nullable":true},"captionsUpdatedAt":{"type":"string","nullable":true},"captionsUpdatedByName":{"type":"string","nullable":true}},"required":["id","accountId","createdAt","type"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createVideos","tags":["Videos"],"x-stainless-method":"videos.create","security":[{"apiKey":[]}],"description":"Create a video record and return a temporary upload target for the original MP4 or WebM file. Upload the file directly using the returned URL and matching content type, then poll the video to observe processing progress. Requires scope: media:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Video title. Defaults to `New Video` when omitted.","nullable":true},"description":{"type":"string","nullable":true},"mimeType":{"type":"string","description":"Content type for the original video upload: `MP4` or `WEBM`.","enum":["MP4","WEBM"],"example":"MP4"}},"required":["mimeType"]}}}},"responses":{"200":{"description":"Created video and direct upload target","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"type":{"type":"string","enum":["Video"]},"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"hasVideoUpload":{"type":"boolean","nullable":true},"captionsUpdatedAt":{"type":"string","nullable":true},"captionsUpdatedByName":{"type":"string","nullable":true},"upload":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"mimeType":{"type":"string","enum":["MP4","WEBM"]}},"required":["url","mimeType"]}},"required":["id","accountId","createdAt","type","upload"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — the account has reached its video count limit.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/videos/{id}":{"get":{"operationId":"findVideos","tags":["Videos"],"x-stainless-method":"videos.retrieve","security":[{"apiKey":[]}],"description":"Fetch a video and its current playback asset availability. The processed video, poster, and transcript are prepared asynchronously after upload. Requires scope: media:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Video details and processing state","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"type":{"type":"string","enum":["Video"]},"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"hasVideoUpload":{"type":"boolean","nullable":true},"captionsUpdatedAt":{"type":"string","nullable":true},"captionsUpdatedByName":{"type":"string","nullable":true},"resolvedValues":{"type":"object","nullable":true,"properties":{"videoStatus":{"type":"object","nullable":true,"properties":{"posterExists":{"type":"boolean","nullable":true},"outputExists":{"type":"boolean","nullable":true},"outputIsCurrent":{"type":"boolean","nullable":true},"transcriptionExists":{"type":"boolean","nullable":true}}},"mediaConvertJob":{"type":"object","nullable":true,"properties":{"status":{"type":"string","nullable":true},"percentComplete":{"type":"number","nullable":true}}},"posterUrl":{"type":"string"},"videoUrl":{"type":"string"},"vttUrl":{"type":"string","nullable":true}},"required":["posterUrl","videoUrl"]}},"required":["id","accountId","createdAt","type"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchVideos","tags":["Videos"],"x-stainless-method":"videos.update","security":[{"apiKey":[]}],"description":"Partially update video metadata. Only fields included in the body change; send `null` to clear a nullable field. Requires scope: media:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"hasVideoUpload":{"type":"boolean","nullable":true}}}}}},"responses":{"200":{"description":"Updated video metadata","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"type":{"type":"string","enum":["Video"]},"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"hasVideoUpload":{"type":"boolean","nullable":true},"captionsUpdatedAt":{"type":"string","nullable":true},"captionsUpdatedByName":{"type":"string","nullable":true}},"required":["id","accountId","createdAt","type"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteVideos","tags":["Videos"],"x-stainless-method":"videos.delete","security":[{"apiKey":[]}],"description":"Delete a video and its related assets. Requires scope: media:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted video","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/videos/{id}/upload":{"post":{"operationId":"uploadVideos","tags":["Videos"],"x-stainless-method":"videos.upload","security":[{"apiKey":[]}],"description":"Return a temporary upload target for replacing this video’s original MP4 or WebM source. Upload the file directly using the returned URL and matching content type, then poll the video to observe processing progress. Requires scope: media:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mimeType":{"type":"string","description":"Content type for the replacement original video: `MP4` or `WEBM`.","enum":["MP4","WEBM"],"example":"MP4"}},"required":["mimeType"]}}}},"responses":{"200":{"description":"Direct upload target for a replacement video source","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"mimeType":{"type":"string","enum":["MP4","WEBM"]}},"required":["url","mimeType"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/videos/{id}/transcript":{"get":{"operationId":"getVideoTranscript","tags":["Videos"],"x-stainless-method":"videos.transcript","security":[{"apiKey":[]}],"description":"Read the current WebVTT transcript. Transcript text is available wherever the video is embedded, so do not include PHI or other confidential information. Requires scope: media:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Current WebVTT transcript","content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","nullable":true}}}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"put":{"operationId":"updateVideoTranscript","tags":["Videos"],"x-stainless-method":"videos.updateTranscript","security":[{"apiKey":[]}],"description":"Replace the transcript with VTT or SRT text. SRT is normalized to WebVTT. Transcript text is available wherever the video is embedded, so do not include PHI or other confidential information. Requires scope: media:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"format":{"type":"string","enum":["SRT","VTT"],"description":"Transcript source format. SRT content is normalized to WebVTT before it is saved."},"content":{"type":"string","minLength":1,"maxLength":1000000,"description":"Transcript text, limited to 1 MB."}},"required":["format","content"]}}}},"responses":{"200":{"description":"Video with updated transcript provenance","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"type":{"type":"string","enum":["Video"]},"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"hasVideoUpload":{"type":"boolean","nullable":true},"captionsUpdatedAt":{"type":"string","nullable":true},"captionsUpdatedByName":{"type":"string","nullable":true}},"required":["id","accountId","createdAt","type"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/videos/analytics":{"get":{"operationId":"getVideoAnalytics","tags":["Videos"],"x-stainless-method":"videos.analytics","security":[{"apiKey":[]}],"description":"Return per-video starts, unique viewers, completion rate, and average watch time for a date window. This derived report uses `limit` and `offset` pagination; `total` is the number of rows returned through the current offset, not a total match count. Requires scope: report:video-analytics","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive UTC start day in `YYYY-MM-DD` format."},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive UTC end day in `YYYY-MM-DD` format."},"required":true,"name":"to","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Maximum number of video rows to return. Defaults to 50."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Zero-based row offset. This report is an intentional offset-pagination exception."},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Account-wide video analytics","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"videoId":{"type":"string","nullable":true},"videoTitle":{"type":"string"},"videoUrl":{"type":"string","nullable":true},"videoStarts":{"type":"integer"},"uniqueViewers":{"type":"integer"},"avgWatchTimeSeconds":{"type":"number"},"completionRate":{"type":"number"},"avgVideoDurationSeconds":{"type":"number"}},"required":["videoTitle","videoStarts","uniqueViewers","avgWatchTimeSeconds","completionRate","avgVideoDurationSeconds"]}},"total":{"type":"integer"},"hasMore":{"type":"boolean"}},"required":["items","total","hasMore"]}}}},"400":{"description":"Bad request — malformed dates, an invalid page size, or a window longer than 90 days.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/videos/{id}/analytics":{"get":{"operationId":"getVideoAnalyticsTimeseries","tags":["Videos"],"x-stainless-method":"videos.analyticsTimeseries","security":[{"apiKey":[]}],"description":"Return daily or hourly starts, unique viewers, completions, and completion rate for one video. Daily windows support up to 90 days; hourly windows support up to 14 days. Requires scope: report:video-analytics","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive UTC start day in `YYYY-MM-DD` format."},"required":true,"name":"from","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive UTC end day in `YYYY-MM-DD` format."},"required":true,"name":"to","in":"query"},{"schema":{"type":"string","enum":["DAILY","HOURLY"],"description":"Bucket size. Defaults to `DAILY`; `HOURLY` supports windows of up to 14 days."},"required":false,"name":"granularity","in":"query"}],"responses":{"200":{"description":"Video engagement time series","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"dateTime":{"type":"string"},"videoStarts":{"type":"integer"},"uniqueViewers":{"type":"integer"},"completions":{"type":"integer"},"completionRate":{"type":"number"}},"required":["dateTime","videoStarts","uniqueViewers","completions","completionRate"]}}},"required":["items"]}}}},"400":{"description":"Bad request — malformed dates or a window longer than the selected granularity allows.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/video-channels":{"get":{"operationId":"listVideoChannels","tags":["Video Channels"],"x-stainless-method":"video_channels.list","security":[{"apiKey":[]}],"description":"List video channels for the account, sorted by name. Supports cursor pagination via `limit` and `cursor`; the limit clamp is 1000 so a single request can return the full set. Entries omit `resolvedValues` — fetch a channel by id for its video count and embed output. Requires scope: videoChannel:list","parameters":[{"schema":{"type":"integer","nullable":true,"description":"Maximum number of channels to return. Defaults to 25; values below 1 are clamped to 1 and values above 1000 are clamped to 1000.","example":25},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor from pagination.nextCursor in the previous response. Do not decode or modify it. Malformed cursors return 400 Bad Request."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"Paginated list of video channels","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"brandColor":{"type":"string","nullable":true},"logoMediaId":{"type":"string","nullable":true},"footerText":{"type":"string","nullable":true},"isPublished":{"type":"boolean","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","name"]}},"pagination":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"}},"required":["hasMore"]}},"required":["entities","pagination"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"createVideoChannels","tags":["Video Channels"],"x-stainless-method":"video_channels.create","security":[{"apiKey":[]}],"description":"Create a video channel. Only `name` is accepted here; set branding and publish it with PATCH, and add videos with `POST /rest/v1/video-channels/{id}/media`. New channels start unpublished, so the page is not reachable until you send `isPublished: true`. Requires scope: videoChannel:create","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Channel name. Case-insensitively unique within the account."}},"required":["name"]}}}},"responses":{"200":{"description":"Created video channel","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"brandColor":{"type":"string","nullable":true},"logoMediaId":{"type":"string","nullable":true},"footerText":{"type":"string","nullable":true},"isPublished":{"type":"boolean","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","name"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — a channel with the same name (case-insensitive) already exists in this account.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/video-channels/{id}":{"get":{"operationId":"findVideoChannels","tags":["Video Channels"],"x-stainless-method":"video_channels.retrieve","security":[{"apiKey":[]}],"description":"Fetch a channel with its branding, publish state, video count, shareable page URL, and paste-ready embed code. Requires scope: videoChannel:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Channel details, video count, and embed output","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"brandColor":{"type":"string","nullable":true},"logoMediaId":{"type":"string","nullable":true},"footerText":{"type":"string","nullable":true},"isPublished":{"type":"boolean","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"resolvedValues":{"type":"object","nullable":true}},"required":["id","accountId","name"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"patch":{"operationId":"patchVideoChannels","tags":["Video Channels"],"x-stainless-method":"video_channels.update","security":[{"apiKey":[]}],"description":"Partially update a channel. Only fields included in the body change; send `null` to clear a nullable field. Sending `isPublished: true` makes the channel page reachable and renders it from the current videos and branding; `false` takes it offline. `logoMediaId` must reference an image in your media library. Requires scope: videoChannel:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New channel name. Must stay case-insensitively unique within the account.","nullable":true},"description":{"type":"string","nullable":true},"brandColor":{"type":"string","description":"Accent color used on the channel page. Any CSS color string.","nullable":true},"logoMediaId":{"type":"string","description":"Id of an image in your media library to show as the channel logo. Must be an image; send `null` to clear it.","nullable":true},"footerText":{"type":"string","nullable":true},"isPublished":{"type":"boolean","description":"Whether the channel page is publicly reachable. Publishing renders the page from the current videos and branding; unpublishing takes it offline.","nullable":true}}}}}},"responses":{"200":{"description":"Updated video channel","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"brandColor":{"type":"string","nullable":true},"logoMediaId":{"type":"string","nullable":true},"footerText":{"type":"string","nullable":true},"isPublished":{"type":"boolean","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"resolvedValues":{"type":"object","nullable":true}},"required":["id","accountId","name"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Conflict — a channel with the same name (case-insensitive) already exists in this account.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"deleteVideoChannels","tags":["Video Channels"],"x-stainless-method":"video_channels.delete","security":[{"apiKey":[]}],"description":"Delete a channel and take its page offline. The videos it listed are not deleted — only their membership in this channel. Requires scope: videoChannel:delete","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted video channel","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/video-channels/{id}/media":{"get":{"operationId":"getVideoChannelMedia","tags":["Video Channels"],"x-stainless-method":"video_channels.media","security":[{"apiKey":[]}],"description":"List the videos in a channel, ordered by their position on the page. Not paginated: a channel holds a bounded set of videos, so the full ordered list is always returned. Videos whose media no longer resolves are omitted rather than returned as broken entries. Requires scope: videoChannel:find","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The channel's videos in display order","content":{"application/json":{"schema":{"type":"object","properties":{"entities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string","nullable":true},"type":{"type":"string","enum":["Video"]},"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"duration":{"type":"number","nullable":true},"width":{"type":"number","nullable":true},"height":{"type":"number","nullable":true},"hasVideoUpload":{"type":"boolean","nullable":true},"captionsUpdatedAt":{"type":"string","nullable":true},"captionsUpdatedByName":{"type":"string","nullable":true}},"required":["id","accountId","createdAt","type"]}}},"required":["entities"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"post":{"operationId":"assignVideoChannelMedia","tags":["Video Channels"],"x-stainless-method":"video_channels.assignMedia","security":[{"apiKey":[]}],"description":"Add a video to a channel. Omit `position` to append it to the end. A video can belong to several channels, so adding it here does not remove it from any other. Calling this again for a video already in the channel updates its position instead of adding a duplicate, and keeps its current slot when `position` is omitted. The returned `id` is a composite membership key, not a UUID. Requires scope: videoChannel:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mediaId":{"type":"string","format":"uuid","description":"Id of the video to add to the channel. Must be a video, not an image."},"position":{"type":"integer","minimum":0,"description":"Zero-based slot in the channel order. Omit to append to the end; omitting it on a video that is already in the channel keeps its current slot."}},"required":["mediaId"]}}}},"responses":{"200":{"description":"The video’s membership in this channel","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"channelId":{"type":"string","nullable":true},"mediaId":{"type":"string"},"position":{"type":"number","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true}},"required":["id","accountId","mediaId"]}}}},"400":{"description":"Bad request — `mediaId` refers to an image rather than a video.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not found — the channel or the video does not exist in this account.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}},"delete":{"operationId":"removeVideoChannelMedia","tags":["Video Channels"],"x-stainless-method":"video_channels.removeMedia","security":[{"apiKey":[]}],"description":"Remove one video from a channel, identified by the `mediaId` query parameter. The video itself is not deleted and stays in any other channel it belongs to. Idempotent — removing a video that is not in the channel succeeds and returns the channel unchanged. Requires scope: videoChannel:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Id of the video to remove from this channel."},"required":true,"name":"mediaId","in":"query"}],"responses":{"200":{"description":"The channel after the video was removed","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"brandColor":{"type":"string","nullable":true},"logoMediaId":{"type":"string","nullable":true},"footerText":{"type":"string","nullable":true},"isPublished":{"type":"boolean","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"resolvedValues":{"type":"object","nullable":true}},"required":["id","accountId","name"]}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}},"/rest/v1/video-channels/{id}/reorder":{"post":{"operationId":"reorderVideoChannels","tags":["Video Channels"],"x-stainless-method":"video_channels.reorder","security":[{"apiKey":[]}],"description":"Set the display order of a channel’s videos. Send every video id currently in the channel in the order you want them shown — index 0 appears first. A partial list, or an id that is not in the channel, returns 400 so a caller working from a stale view learns it is out of date instead of getting a partial write. Requires scope: videoChannel:update","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mediaIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Every video id currently in the channel, in the order you want them shown. Partial lists and ids that are not in the channel are rejected."}},"required":["mediaIds"]}}}},"responses":{"200":{"description":"The channel after reordering","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"accountId":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"brandColor":{"type":"string","nullable":true},"logoMediaId":{"type":"string","nullable":true},"footerText":{"type":"string","nullable":true},"isPublished":{"type":"boolean","nullable":true},"createdAt":{"type":"string","nullable":true},"updatedAt":{"type":"string","nullable":true},"resolvedValues":{"type":"object","nullable":true}},"required":["id","accountId","name"]}}}},"400":{"description":"Bad request — `mediaIds` omitted a video in the channel or included an id that is not in it.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized — missing, invalid, or expired API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"403":{"description":"Forbidden — API key does not have the required scope","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}},"required":["error"]}}}}}}}}}