[{"data":1,"prerenderedAt":2270},["ShallowReactive",2],{"docs-\u002Fapi\u002Fai-experiments":3},{"id":4,"title":5,"body":6,"description":2263,"extension":2264,"meta":2265,"navigation":756,"path":2266,"seo":2267,"stem":2268,"__hash__":2269},"docs\u002Fdocs\u002Fapi\u002Fai-experiments.md","AI Experiments API",{"type":7,"value":8,"toc":2251},"minimark",[9,13,32,35,38,43,87,94,96,100,103,189,200,207,209,213,216,223,229,232,397,400,506,509,576,582,607,615,629,641,646,657,716,850,861,865,868,1009,1019,1067,1071,1074,1126,1143,1147,1154,1247,1260,1271,1278,1380,1386,1461,1465,1475,1506,1514,1518,1527,1555,1558,1603,1609,1663,1670,1759,1770,1782,1786,1840,1842,1846,1864,1867,2205,2218,2220,2224,2247],[10,11,5],"h1",{"id":12},"ai-experiments-api",[14,15,16],"blockquote",{},[17,18,19,23,24,31],"p",{},[20,21,22],"strong",{},"Preview feature."," AI Experiments are in active preview. Conversion event tracking is intentionally minimal: one public ingest endpoint, an aggregation that lives next to existing exposures, and no historical aggregation jobs yet. See the ",[25,26,30],"a",{"href":27,"rel":28},"https:\u002F\u002Fstatus.cachely.io",[29],"nofollow","Cachely status page"," for current limitations.",[17,33,34],{},"AI Experiments split traffic between AI transform profiles (or \"control\" — no transform) on requests that match a route pattern. The proxy assigns a variant deterministically per visitor and attaches headers identifying the experiment, the variant, and the profile applied. Your frontend reads those headers, optionally stores them per session, and can later POST a conversion event so the dashboard can compute conversion rate per variant.",[36,37],"hr",{},[39,40,42],"h2",{"id":41},"how-it-works","How it works",[44,45,46,55,62,65,72],"ol",{},[47,48,49,50,54],"li",{},"Configure an experiment in the dashboard with a route pattern and 2+ variants. Each variant points at a transform profile or is ",[51,52,53],"code",{},"control"," (no transform).",[47,56,57,58,61],{},"When a request matches an active experiment, the worker picks a variant by weighted hash of ",[51,59,60],{},"cf-connecting-ip + user-agent"," (salted by experiment id) and applies the variant's profile.",[47,63,64],{},"The response carries the experiment + variant headers (below) so your client knows which variant served the user.",[47,66,67,68,71],{},"After a user action (CTA click, signup, purchase, etc.), your frontend POSTs ",[51,69,70],{},"\u002Fapi\u002Fai-experiments\u002Fevents"," with the experiment + variant attribution and an event name.",[47,73,74,75,78,79,82,83,86],{},"The dashboard's Exposures view aggregates ",[51,76,77],{},"request_logs"," for exposures and ",[51,80,81],{},"ai_experiment_events"," for events, and divides to show ",[20,84,85],{},"conversion rate per variant",".",[17,88,89,90,93],{},"Explicit ",[51,91,92],{},"?transform=\u003Cprofile>"," requests bypass experiment matching entirely. Those responses do not carry experiment headers, and your frontend SHOULD NOT submit events for them.",[36,95],{},[39,97,99],{"id":98},"response-headers","Response headers",[17,101,102],{},"The worker sets the following headers on every response that ran through an experiment:",[104,105,106,119],"table",{},[107,108,109],"thead",{},[110,111,112,116],"tr",{},[113,114,115],"th",{},"Header",[113,117,118],{},"Description",[120,121,122,133,143,170],"tbody",{},[110,123,124,130],{},[125,126,127],"td",{},[51,128,129],{},"x-cachely-experiment-id",[125,131,132],{},"Numeric id of the matched experiment.",[110,134,135,140],{},[125,136,137],{},[51,138,139],{},"x-cachely-variant-id",[125,141,142],{},"Numeric id of the assigned variant.",[110,144,145,150],{},[125,146,147],{},[51,148,149],{},"x-cachely-variant-key",[125,151,152,153,155,156,155,159,162,163],{},"The variant's stable string key (e.g. ",[51,154,53],{},", ",[51,157,158],{},"serbian",[51,160,161],{},"serbian-v2","). ",[20,164,165,166,169],{},"Use this as ",[51,167,168],{},"variantKey"," when submitting events.",[110,171,172,177],{},[125,173,174],{},[51,175,176],{},"x-cachely-experiment-profile",[125,178,179,180,182,183],{},"Transform profile applied, or the literal ",[51,181,53],{}," for control variants. ",[20,184,185,186,188],{},"Do not use this as ",[51,187,168],{}," — profile and key may diverge.",[17,190,191,192,195,196,199],{},"For control variants, the worker additionally sets ",[51,193,194],{},"X-AI-Transform: skipped"," and ",[51,197,198],{},"X-AI-Transform-Skip-Reason: experiment-control"," so debug tooling can distinguish \"control branch ran\" from \"no experiment matched\".",[17,201,202,203,206],{},"All headers are listed in ",[51,204,205],{},"Access-Control-Expose-Headers"," so cross-origin browsers can read them.",[36,208],{},[39,210,212],{"id":211},"tracking-conversion-events-from-your-site","Tracking conversion events from your site",[17,214,215],{},"The endpoint is unauthenticated and CORS-open — call it directly from the browser after the Cachely fetch completes. The validation chain (real tenant → real experiment → real variant whose stored key matches) is the security boundary.",[217,218,220],"h3",{"id":219},"post-apiai-experimentsevents",[51,221,222],{},"POST \u002Fapi\u002Fai-experiments\u002Fevents",[17,224,225,226,86],{},"Submit a conversion event for an experiment variant. Public, unauthenticated, wide-open CORS. Hosted at ",[51,227,228],{},"https:\u002F\u002Fapp.cachely.io",[17,230,231],{},"Request body (JSON):",[233,234,239],"pre",{"className":235,"code":236,"language":237,"meta":238,"style":238},"language-json shiki shiki-themes github-dark","{\n  \"tenantSlug\": \"prismic\",\n  \"experimentId\": 1,\n  \"variantId\": 2,\n  \"variantKey\": \"serbian\",\n  \"eventName\": \"homepage_hero_cta_click\",\n  \"visitorId\": \"anon_abc123\",\n  \"sessionId\": \"sess_xyz789\",\n  \"url\": \"https:\u002F\u002Fexample.com\u002F\",\n  \"referrer\": \"https:\u002F\u002Fgoogle.com\u002F\",\n  \"metadata\": { \"cta\": \"hero_primary\" }\n}\n","json","",[51,240,241,250,267,280,293,306,319,332,345,358,371,391],{"__ignoreMap":238},[242,243,246],"span",{"class":244,"line":245},"line",1,[242,247,249],{"class":248},"s95oV","{\n",[242,251,253,257,260,264],{"class":244,"line":252},2,[242,254,256],{"class":255},"sDLfK","  \"tenantSlug\"",[242,258,259],{"class":248},": ",[242,261,263],{"class":262},"sU2Wk","\"prismic\"",[242,265,266],{"class":248},",\n",[242,268,270,273,275,278],{"class":244,"line":269},3,[242,271,272],{"class":255},"  \"experimentId\"",[242,274,259],{"class":248},[242,276,277],{"class":255},"1",[242,279,266],{"class":248},[242,281,283,286,288,291],{"class":244,"line":282},4,[242,284,285],{"class":255},"  \"variantId\"",[242,287,259],{"class":248},[242,289,290],{"class":255},"2",[242,292,266],{"class":248},[242,294,296,299,301,304],{"class":244,"line":295},5,[242,297,298],{"class":255},"  \"variantKey\"",[242,300,259],{"class":248},[242,302,303],{"class":262},"\"serbian\"",[242,305,266],{"class":248},[242,307,309,312,314,317],{"class":244,"line":308},6,[242,310,311],{"class":255},"  \"eventName\"",[242,313,259],{"class":248},[242,315,316],{"class":262},"\"homepage_hero_cta_click\"",[242,318,266],{"class":248},[242,320,322,325,327,330],{"class":244,"line":321},7,[242,323,324],{"class":255},"  \"visitorId\"",[242,326,259],{"class":248},[242,328,329],{"class":262},"\"anon_abc123\"",[242,331,266],{"class":248},[242,333,335,338,340,343],{"class":244,"line":334},8,[242,336,337],{"class":255},"  \"sessionId\"",[242,339,259],{"class":248},[242,341,342],{"class":262},"\"sess_xyz789\"",[242,344,266],{"class":248},[242,346,348,351,353,356],{"class":244,"line":347},9,[242,349,350],{"class":255},"  \"url\"",[242,352,259],{"class":248},[242,354,355],{"class":262},"\"https:\u002F\u002Fexample.com\u002F\"",[242,357,266],{"class":248},[242,359,361,364,366,369],{"class":244,"line":360},10,[242,362,363],{"class":255},"  \"referrer\"",[242,365,259],{"class":248},[242,367,368],{"class":262},"\"https:\u002F\u002Fgoogle.com\u002F\"",[242,370,266],{"class":248},[242,372,374,377,380,383,385,388],{"class":244,"line":373},11,[242,375,376],{"class":255},"  \"metadata\"",[242,378,379],{"class":248},": { ",[242,381,382],{"class":255},"\"cta\"",[242,384,259],{"class":248},[242,386,387],{"class":262},"\"hero_primary\"",[242,389,390],{"class":248}," }\n",[242,392,394],{"class":244,"line":393},12,[242,395,396],{"class":248},"}\n",[17,398,399],{},"Validation:",[104,401,402,412],{},[107,403,404],{},[110,405,406,409],{},[113,407,408],{},"Field",[113,410,411],{},"Rule",[120,413,414,424,434,444,457,470,483,496],{},[110,415,416,421],{},[125,417,418],{},[51,419,420],{},"tenantSlug",[125,422,423],{},"Required. Must match an existing tenant.",[110,425,426,431],{},[125,427,428],{},[51,429,430],{},"experimentId",[125,432,433],{},"Required. Positive integer. Must belong to the tenant.",[110,435,436,441],{},[125,437,438],{},[51,439,440],{},"variantId",[125,442,443],{},"Required. Positive integer. Must belong to the experiment.",[110,445,446,450],{},[125,447,448],{},[51,449,168],{},[125,451,452,453,456],{},"Required. Must equal the variant's stored ",[51,454,455],{},"key"," (mismatch → 400).",[110,458,459,464],{},[125,460,461],{},[51,462,463],{},"eventName",[125,465,466,467,86],{},"Required. Matches ",[51,468,469],{},"\u002F^[a-zA-Z0-9_.:-]{1,80}$\u002F",[110,471,472,480],{},[125,473,474,155,477],{},[51,475,476],{},"visitorId",[51,478,479],{},"sessionId",[125,481,482],{},"Optional. ≤ 200 chars each.",[110,484,485,493],{},[125,486,487,155,490],{},[51,488,489],{},"url",[51,491,492],{},"referrer",[125,494,495],{},"Optional. ≤ 2048 chars.",[110,497,498,503],{},[125,499,500],{},[51,501,502],{},"metadata",[125,504,505],{},"Optional JSON object. Serialised body ≤ 4 KB.",[17,507,508],{},"Responses:",[104,510,511,521],{},[107,512,513],{},[110,514,515,518],{},[113,516,517],{},"Status",[113,519,520],{},"Meaning",[120,522,523,536,552,562],{},[110,524,525,530],{},[125,526,527],{},[51,528,529],{},"200",[125,531,532,535],{},[51,533,534],{},"{ ok: true, id }"," — event recorded.",[110,537,538,543],{},[125,539,540],{},[51,541,542],{},"400",[125,544,545,546,548,549,551],{},"Payload failed validation, or ",[51,547,440],{},"\u002F",[51,550,168],{}," mismatch.",[110,553,554,559],{},[125,555,556],{},[51,557,558],{},"404",[125,560,561],{},"Tenant or experiment not found.",[110,563,564,569],{},[125,565,566],{},[51,567,568],{},"429",[125,570,571,572,575],{},"Rate limit (60 events \u002F minute \u002F IP+tenantSlug). ",[51,573,574],{},"Retry-After"," header included.",[217,577,579],{"id":578},"options-apiai-experimentsevents",[51,580,581],{},"OPTIONS \u002Fapi\u002Fai-experiments\u002Fevents",[17,583,584,585,588,589,155,592,595,596,599,600,603,604,86],{},"CORS preflight for the POST above. Returns ",[51,586,587],{},"204"," with ",[51,590,591],{},"Access-Control-Allow-Origin: *",[51,593,594],{},"Access-Control-Allow-Methods: POST, OPTIONS",", and ",[51,597,598],{},"Access-Control-Allow-Headers"," echoing the request's ",[51,601,602],{},"Access-Control-Request-Headers",". Cached for 24 hours via ",[51,605,606],{},"Access-Control-Max-Age",[217,608,610,611,614],{"id":609},"recommended-cachely-iosdk-integration","Recommended: ",[51,612,613],{},"@cachely-io\u002Fsdk"," integration",[17,616,617,618,620,621,624,625,628],{},"The ",[51,619,613],{}," (≥ 0.10) ships a built-in tracker that captures the experiment headers from the ",[20,622,623],{},"same"," Cachely fetch that rendered the content — no probe requests, no page-level header plumbing, no manual ",[51,626,627],{},"sessionStorage"," wiring.",[14,630,631],{},[17,632,633,636,637,640],{},[20,634,635],{},"Why no probe?"," A separate request \"just to capture headers\" can be assigned to a different variant than the one that actually rendered. The dashboard then attributes the conversion to the wrong variant. The SDK avoids this by inspecting headers on the response objects produced by ",[51,638,639],{},"createCachelyPrismicClient"," (and friends).",[642,643,645],"h4",{"id":644},"nuxt-recommended-composable-module","Nuxt (recommended) — composable + module",[17,647,648,649,652,653,656],{},"Since SDK 0.12.0, ",[51,650,651],{},"npx @cachely-io\u002Fsdk setup --provider prismic --experiments"," generates the composable below. Register the Cachely Nuxt module so ",[51,654,655],{},"useCachelyExperiments()"," resolves at build time:",[233,658,662],{"className":659,"code":660,"language":661,"meta":238,"style":238},"language-ts shiki shiki-themes github-dark","\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  modules: ['@cachely-io\u002Fsdk\u002Fnuxt'],\n  cachely: { tenant: 'your-tenant' }, \u002F\u002F required for the module to construct the tracker\n})\n","ts",[51,663,664,670,686,697,711],{"__ignoreMap":238},[242,665,666],{"class":244,"line":245},[242,667,669],{"class":668},"sAwPA","\u002F\u002F nuxt.config.ts\n",[242,671,672,676,679,683],{"class":244,"line":252},[242,673,675],{"class":674},"snl16","export",[242,677,678],{"class":674}," default",[242,680,682],{"class":681},"svObZ"," defineNuxtConfig",[242,684,685],{"class":248},"({\n",[242,687,688,691,694],{"class":244,"line":269},[242,689,690],{"class":248},"  modules: [",[242,692,693],{"class":262},"'@cachely-io\u002Fsdk\u002Fnuxt'",[242,695,696],{"class":248},"],\n",[242,698,699,702,705,708],{"class":244,"line":282},[242,700,701],{"class":248},"  cachely: { tenant: ",[242,703,704],{"class":262},"'your-tenant'",[242,706,707],{"class":248}," }, ",[242,709,710],{"class":668},"\u002F\u002F required for the module to construct the tracker\n",[242,712,713],{"class":244,"line":295},[242,714,715],{"class":248},"})\n",[233,717,719],{"className":659,"code":718,"language":661,"meta":238,"style":238},"\u002F\u002F composables\u002FuseCachelyPrismicClient.ts (generated by `setup --provider prismic --experiments`)\nimport { createCachelyPrismicClient } from '@cachely-io\u002Fsdk\u002Fprismic'\nimport { repositoryName } from '~\u002Fslicemachine.config.json'\n\nexport function useCachelyPrismicClient() {\n  \u002F\u002F useCachelyExperiments() is auto-imported by `@cachely-io\u002Fsdk\u002Fnuxt`.\n  \u002F\u002F Call it INSIDE the composable so each request gets its own per-request tracker\n  \u002F\u002F (the Nuxt plugin attaches a fresh instance via useNuxtApp()). Calling at module\n  \u002F\u002F scope would resolve to a single shared instance and break SSR isolation.\n  const experiments = useCachelyExperiments()\n  return createCachelyPrismicClient({\n    repositoryName,\n    tenant: 'your-tenant',\n    experiments,\n  })\n}\n",[51,720,721,726,740,752,758,771,776,781,786,791,808,818,823,833,839,845],{"__ignoreMap":238},[242,722,723],{"class":244,"line":245},[242,724,725],{"class":668},"\u002F\u002F composables\u002FuseCachelyPrismicClient.ts (generated by `setup --provider prismic --experiments`)\n",[242,727,728,731,734,737],{"class":244,"line":252},[242,729,730],{"class":674},"import",[242,732,733],{"class":248}," { createCachelyPrismicClient } ",[242,735,736],{"class":674},"from",[242,738,739],{"class":262}," '@cachely-io\u002Fsdk\u002Fprismic'\n",[242,741,742,744,747,749],{"class":244,"line":269},[242,743,730],{"class":674},[242,745,746],{"class":248}," { repositoryName } ",[242,748,736],{"class":674},[242,750,751],{"class":262}," '~\u002Fslicemachine.config.json'\n",[242,753,754],{"class":244,"line":282},[242,755,757],{"emptyLinePlaceholder":756},true,"\n",[242,759,760,762,765,768],{"class":244,"line":295},[242,761,675],{"class":674},[242,763,764],{"class":674}," function",[242,766,767],{"class":681}," useCachelyPrismicClient",[242,769,770],{"class":248},"() {\n",[242,772,773],{"class":244,"line":308},[242,774,775],{"class":668},"  \u002F\u002F useCachelyExperiments() is auto-imported by `@cachely-io\u002Fsdk\u002Fnuxt`.\n",[242,777,778],{"class":244,"line":321},[242,779,780],{"class":668},"  \u002F\u002F Call it INSIDE the composable so each request gets its own per-request tracker\n",[242,782,783],{"class":244,"line":334},[242,784,785],{"class":668},"  \u002F\u002F (the Nuxt plugin attaches a fresh instance via useNuxtApp()). Calling at module\n",[242,787,788],{"class":244,"line":347},[242,789,790],{"class":668},"  \u002F\u002F scope would resolve to a single shared instance and break SSR isolation.\n",[242,792,793,796,799,802,805],{"class":244,"line":360},[242,794,795],{"class":674},"  const",[242,797,798],{"class":255}," experiments",[242,800,801],{"class":674}," =",[242,803,804],{"class":681}," useCachelyExperiments",[242,806,807],{"class":248},"()\n",[242,809,810,813,816],{"class":244,"line":373},[242,811,812],{"class":674},"  return",[242,814,815],{"class":681}," createCachelyPrismicClient",[242,817,685],{"class":248},[242,819,820],{"class":244,"line":393},[242,821,822],{"class":248},"    repositoryName,\n",[242,824,826,829,831],{"class":244,"line":825},13,[242,827,828],{"class":248},"    tenant: ",[242,830,704],{"class":262},[242,832,266],{"class":248},[242,834,836],{"class":244,"line":835},14,[242,837,838],{"class":248},"    experiments,\n",[242,840,842],{"class":244,"line":841},15,[242,843,844],{"class":248},"  })\n",[242,846,848],{"class":244,"line":847},16,[242,849,396],{"class":248},[17,851,852,853,856,857,860],{},"The Nuxt module also calls ",[51,854,855],{},"tracker.autoTrack()"," automatically after hydration, so ",[51,858,859],{},"[data-cachely-track]"," clicks work out of the box — no extra code.",[642,862,864],{"id":863},"non-nuxt-manual-setup-at-module-scope","Non-Nuxt — manual setup at module scope",[17,866,867],{},"For non-Nuxt projects (Next.js, Astro, Express, etc.), construct the tracker yourself wherever you build your Cachely client. There is no auto-import outside Nuxt.",[233,869,873],{"className":870,"code":871,"language":872,"meta":238,"style":238},"language-js shiki shiki-themes github-dark","\u002F\u002F e.g. lib\u002Fprismic.ts (Next.js)\nimport {\n  createCachelyPrismicClient,\n  createCachelyExperimentTracker,\n} from '@cachely-io\u002Fsdk\u002Fprismic'\nimport { repositoryName } from '~\u002Fslicemachine.config.json'\n\nexport const cachelyExperiments = createCachelyExperimentTracker({\n  tenant: 'your-tenant',\n})\n\nexport const cachelyPrismicClient = createCachelyPrismicClient({\n  repositoryName,\n  tenant: 'your-tenant',\n  experiments: cachelyExperiments, \u002F\u002F ← wires header capture into every Prismic fetch\n})\n\nexport default cachelyPrismicClient\n","js",[51,874,875,880,887,892,897,906,916,920,937,946,950,954,969,974,982,990,994,999],{"__ignoreMap":238},[242,876,877],{"class":244,"line":245},[242,878,879],{"class":668},"\u002F\u002F e.g. lib\u002Fprismic.ts (Next.js)\n",[242,881,882,884],{"class":244,"line":252},[242,883,730],{"class":674},[242,885,886],{"class":248}," {\n",[242,888,889],{"class":244,"line":269},[242,890,891],{"class":248},"  createCachelyPrismicClient,\n",[242,893,894],{"class":244,"line":282},[242,895,896],{"class":248},"  createCachelyExperimentTracker,\n",[242,898,899,902,904],{"class":244,"line":295},[242,900,901],{"class":248},"} ",[242,903,736],{"class":674},[242,905,739],{"class":262},[242,907,908,910,912,914],{"class":244,"line":308},[242,909,730],{"class":674},[242,911,746],{"class":248},[242,913,736],{"class":674},[242,915,751],{"class":262},[242,917,918],{"class":244,"line":321},[242,919,757],{"emptyLinePlaceholder":756},[242,921,922,924,927,930,932,935],{"class":244,"line":334},[242,923,675],{"class":674},[242,925,926],{"class":674}," const",[242,928,929],{"class":255}," cachelyExperiments",[242,931,801],{"class":674},[242,933,934],{"class":681}," createCachelyExperimentTracker",[242,936,685],{"class":248},[242,938,939,942,944],{"class":244,"line":347},[242,940,941],{"class":248},"  tenant: ",[242,943,704],{"class":262},[242,945,266],{"class":248},[242,947,948],{"class":244,"line":360},[242,949,715],{"class":248},[242,951,952],{"class":244,"line":373},[242,953,757],{"emptyLinePlaceholder":756},[242,955,956,958,960,963,965,967],{"class":244,"line":393},[242,957,675],{"class":674},[242,959,926],{"class":674},[242,961,962],{"class":255}," cachelyPrismicClient",[242,964,801],{"class":674},[242,966,815],{"class":681},[242,968,685],{"class":248},[242,970,971],{"class":244,"line":825},[242,972,973],{"class":248},"  repositoryName,\n",[242,975,976,978,980],{"class":244,"line":835},[242,977,941],{"class":248},[242,979,704],{"class":262},[242,981,266],{"class":248},[242,983,984,987],{"class":244,"line":841},[242,985,986],{"class":248},"  experiments: cachelyExperiments, ",[242,988,989],{"class":668},"\u002F\u002F ← wires header capture into every Prismic fetch\n",[242,991,992],{"class":244,"line":847},[242,993,715],{"class":248},[242,995,997],{"class":244,"line":996},17,[242,998,757],{"emptyLinePlaceholder":756},[242,1000,1002,1004,1006],{"class":244,"line":1001},18,[242,1003,675],{"class":674},[242,1005,678],{"class":674},[242,1007,1008],{"class":248}," cachelyPrismicClient\n",[17,1010,1011,1012,1015,1016,1018],{},"When ",[51,1013,1014],{},"experiments"," is omitted, ",[51,1017,639],{}," behaves exactly as before — no header inspection, no extra wrapping.",[14,1020,1021],{},[17,1022,1023,1030,1031,1033,1034,1037,1038,1041,1042,1044,1045,1048,1049,1052,1053,1056,1057,1060,1061,1064,1065,86],{},[20,1024,1025,1026,1029],{},"Why not the canonical ",[51,1027,1028],{},"app\u002Fprismic\u002Fclient.js","?"," The CLI's ",[51,1032,1028],{}," template (written when you opt into ",[51,1035,1036],{},"--patch-client",") uses ",[51,1039,1040],{},"createCachelyFetch"," directly, which does not accept an ",[51,1043,1014],{}," tracker. That template captures no assignment headers. If you want per-request experiment capture in a Nuxt project, generate the composable (",[51,1046,1047],{},"--composables"," or ",[51,1050,1051],{},"--composables --patch-client",", which is the default for ",[51,1054,1055],{},"--yes",") and call ",[51,1058,1059],{},"useCachelyPrismicClient()"," from your pages. The CLI prints a loud warning if you request ",[51,1062,1063],{},"--patch-client --experiments"," without ",[51,1066,1047],{},[642,1068,1070],{"id":1069},"a-recommended-explicit-tracking","A. Recommended: explicit tracking",[17,1072,1073],{},"Anywhere in your app, after the page has rendered (so the Prismic fetch has populated the assignment):",[233,1075,1077],{"className":870,"code":1076,"language":872,"meta":238,"style":238},"import { cachelyExperiments } from '@\u002Fapp\u002Fprismic\u002Fclient'\n\ncachelyExperiments.track('homepage_hero_cta_click', {\n  cta: 'hero_primary',\n})\n",[51,1078,1079,1091,1095,1112,1122],{"__ignoreMap":238},[242,1080,1081,1083,1086,1088],{"class":244,"line":245},[242,1082,730],{"class":674},[242,1084,1085],{"class":248}," { cachelyExperiments } ",[242,1087,736],{"class":674},[242,1089,1090],{"class":262}," '@\u002Fapp\u002Fprismic\u002Fclient'\n",[242,1092,1093],{"class":244,"line":252},[242,1094,757],{"emptyLinePlaceholder":756},[242,1096,1097,1100,1103,1106,1109],{"class":244,"line":269},[242,1098,1099],{"class":248},"cachelyExperiments.",[242,1101,1102],{"class":681},"track",[242,1104,1105],{"class":248},"(",[242,1107,1108],{"class":262},"'homepage_hero_cta_click'",[242,1110,1111],{"class":248},", {\n",[242,1113,1114,1117,1120],{"class":244,"line":282},[242,1115,1116],{"class":248},"  cta: ",[242,1118,1119],{"class":262},"'hero_primary'",[242,1121,266],{"class":248},[242,1123,1124],{"class":244,"line":295},[242,1125,715],{"class":248},[17,1127,1128,1131,1132,1135,1136,1139,1140,86],{},[51,1129,1130],{},"track()"," is fire-and-forget: it returns a ",[51,1133,1134],{},"Promise\u003C{ ok, id?, error? }>"," for diagnostics but never throws. When no assignment was captured (e.g. the visitor opened the page with ",[51,1137,1138],{},"?transform="," and bypassed the experiment), it silently no-ops and resolves with ",[51,1141,1142],{},"{ ok: false, error: 'no_assignment' }",[642,1144,1146],{"id":1145},"b-recommended-zero-js-cta-tracking-with-data-attributes","B. Recommended: zero-JS CTA tracking with data attributes",[17,1148,1149,1150,1153],{},"Call ",[51,1151,1152],{},"autoTrack()"," once on the client and let HTML drive the events:",[233,1155,1159],{"className":1156,"code":1157,"language":1158,"meta":238,"style":238},"language-html shiki shiki-themes github-dark","\u003Cscript>\n  \u002F\u002F Browser-only — call inside onMounted \u002F componentDidMount \u002F on the client\n  cachelyExperiments.autoTrack()\n\u003C\u002Fscript>\n\n\u003Cbutton\n  data-cachely-track=\"homepage_hero_cta_click\"\n  data-cachely-meta-cta=\"hero_primary\"\n>\n  Track demo CTA\n\u003C\u002Fbutton>\n","html",[51,1160,1161,1173,1178,1188,1197,1201,1208,1219,1229,1233,1238],{"__ignoreMap":238},[242,1162,1163,1166,1170],{"class":244,"line":245},[242,1164,1165],{"class":248},"\u003C",[242,1167,1169],{"class":1168},"s4JwU","script",[242,1171,1172],{"class":248},">\n",[242,1174,1175],{"class":244,"line":252},[242,1176,1177],{"class":668},"  \u002F\u002F Browser-only — call inside onMounted \u002F componentDidMount \u002F on the client\n",[242,1179,1180,1183,1186],{"class":244,"line":269},[242,1181,1182],{"class":248},"  cachelyExperiments.",[242,1184,1185],{"class":681},"autoTrack",[242,1187,807],{"class":248},[242,1189,1190,1193,1195],{"class":244,"line":282},[242,1191,1192],{"class":248},"\u003C\u002F",[242,1194,1169],{"class":1168},[242,1196,1172],{"class":248},[242,1198,1199],{"class":244,"line":295},[242,1200,757],{"emptyLinePlaceholder":756},[242,1202,1203,1205],{"class":244,"line":308},[242,1204,1165],{"class":248},[242,1206,1207],{"class":1168},"button\n",[242,1209,1210,1213,1216],{"class":244,"line":321},[242,1211,1212],{"class":681},"  data-cachely-track",[242,1214,1215],{"class":248},"=",[242,1217,1218],{"class":262},"\"homepage_hero_cta_click\"\n",[242,1220,1221,1224,1226],{"class":244,"line":334},[242,1222,1223],{"class":681},"  data-cachely-meta-cta",[242,1225,1215],{"class":248},[242,1227,1228],{"class":262},"\"hero_primary\"\n",[242,1230,1231],{"class":244,"line":347},[242,1232,1172],{"class":248},[242,1234,1235],{"class":244,"line":360},[242,1236,1237],{"class":248},"  Track demo CTA\n",[242,1239,1240,1242,1245],{"class":244,"line":373},[242,1241,1192],{"class":248},[242,1243,1244],{"class":1168},"button",[242,1246,1172],{"class":248},[17,1248,1249,1251,1252,1255,1256,1259],{},[51,1250,1152],{}," attaches ",[20,1253,1254],{},"one"," delegated click listener and is idempotent — calling it twice does not double-fire events. Metadata keys are derived from ",[51,1257,1258],{},"data-cachely-meta-*"," attributes (the prefix is stripped, value kept as a string).",[17,1261,1262,1263,1266,1267,1270],{},"It is fire-and-forget by design: the listener is ",[51,1264,1265],{},"passive",", never calls ",[51,1268,1269],{},"preventDefault",", and never awaits the network. Link \u002F button navigation is never delayed.",[17,1272,1273,1274,1277],{},"In Nuxt (with ",[51,1275,1276],{},"@cachely-io\u002Fsdk\u002Fnuxt"," registered):",[233,1279,1283],{"className":1280,"code":1281,"language":1282,"meta":238,"style":238},"language-vue shiki shiki-themes github-dark","\u003Cscript setup>\n\u002F\u002F useCachelyExperiments() is auto-imported by the module, and the module\n\u002F\u002F already calls tracker.autoTrack() after hydration — no onMounted call needed.\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton\n    data-cachely-track=\"homepage_hero_cta_click\"\n    data-cachely-meta-cta=\"fixed_demo_cta\"\n  >\n    Track demo CTA\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n","vue",[51,1284,1285,1296,1301,1306,1314,1318,1327,1334,1343,1353,1358,1363,1372],{"__ignoreMap":238},[242,1286,1287,1289,1291,1294],{"class":244,"line":245},[242,1288,1165],{"class":248},[242,1290,1169],{"class":1168},[242,1292,1293],{"class":681}," setup",[242,1295,1172],{"class":248},[242,1297,1298],{"class":244,"line":252},[242,1299,1300],{"class":668},"\u002F\u002F useCachelyExperiments() is auto-imported by the module, and the module\n",[242,1302,1303],{"class":244,"line":269},[242,1304,1305],{"class":668},"\u002F\u002F already calls tracker.autoTrack() after hydration — no onMounted call needed.\n",[242,1307,1308,1310,1312],{"class":244,"line":282},[242,1309,1192],{"class":248},[242,1311,1169],{"class":1168},[242,1313,1172],{"class":248},[242,1315,1316],{"class":244,"line":295},[242,1317,757],{"emptyLinePlaceholder":756},[242,1319,1320,1322,1325],{"class":244,"line":308},[242,1321,1165],{"class":248},[242,1323,1324],{"class":1168},"template",[242,1326,1172],{"class":248},[242,1328,1329,1332],{"class":244,"line":321},[242,1330,1331],{"class":248},"  \u003C",[242,1333,1207],{"class":1168},[242,1335,1336,1339,1341],{"class":244,"line":334},[242,1337,1338],{"class":681},"    data-cachely-track",[242,1340,1215],{"class":248},[242,1342,1218],{"class":262},[242,1344,1345,1348,1350],{"class":244,"line":347},[242,1346,1347],{"class":681},"    data-cachely-meta-cta",[242,1349,1215],{"class":248},[242,1351,1352],{"class":262},"\"fixed_demo_cta\"\n",[242,1354,1355],{"class":244,"line":360},[242,1356,1357],{"class":248},"  >\n",[242,1359,1360],{"class":244,"line":373},[242,1361,1362],{"class":248},"    Track demo CTA\n",[242,1364,1365,1368,1370],{"class":244,"line":393},[242,1366,1367],{"class":248},"  \u003C\u002F",[242,1369,1244],{"class":1168},[242,1371,1172],{"class":248},[242,1373,1374,1376,1378],{"class":244,"line":825},[242,1375,1192],{"class":248},[242,1377,1324],{"class":1168},[242,1379,1172],{"class":248},[17,1381,1382,1383,1385],{},"If you need to call ",[51,1384,1130],{}," explicitly from a Nuxt component:",[233,1387,1389],{"className":1280,"code":1388,"language":1282,"meta":238,"style":238},"\u003Cscript setup>\nconst experiments = useCachelyExperiments()\n\nfunction onCustomCta() {\n  experiments.track('custom_event', { source: 'sidebar' })\n}\n\u003C\u002Fscript>\n",[51,1390,1391,1401,1414,1418,1428,1449,1453],{"__ignoreMap":238},[242,1392,1393,1395,1397,1399],{"class":244,"line":245},[242,1394,1165],{"class":248},[242,1396,1169],{"class":1168},[242,1398,1293],{"class":681},[242,1400,1172],{"class":248},[242,1402,1403,1406,1408,1410,1412],{"class":244,"line":252},[242,1404,1405],{"class":674},"const",[242,1407,798],{"class":255},[242,1409,801],{"class":674},[242,1411,804],{"class":681},[242,1413,807],{"class":248},[242,1415,1416],{"class":244,"line":269},[242,1417,757],{"emptyLinePlaceholder":756},[242,1419,1420,1423,1426],{"class":244,"line":282},[242,1421,1422],{"class":674},"function",[242,1424,1425],{"class":681}," onCustomCta",[242,1427,770],{"class":248},[242,1429,1430,1433,1435,1437,1440,1443,1446],{"class":244,"line":295},[242,1431,1432],{"class":248},"  experiments.",[242,1434,1102],{"class":681},[242,1436,1105],{"class":248},[242,1438,1439],{"class":262},"'custom_event'",[242,1441,1442],{"class":248},", { source: ",[242,1444,1445],{"class":262},"'sidebar'",[242,1447,1448],{"class":248}," })\n",[242,1450,1451],{"class":244,"line":308},[242,1452,396],{"class":248},[242,1454,1455,1457,1459],{"class":244,"line":321},[242,1456,1192],{"class":248},[242,1458,1169],{"class":1168},[242,1460,1172],{"class":248},[642,1462,1464],{"id":1463},"c-advanced-manual-fallback","C. Advanced \u002F manual fallback",[17,1466,1467,1468,1471,1472,1474],{},"The SDK reads the four ",[51,1469,1470],{},"x-cachely-*"," headers from every response that flows through the client and stores the latest active assignment for the current session in memory and ",[51,1473,627],{},". You don't need to wire anything else. If you must integrate from a context that does not use the SDK client, the tracker exposes:",[1476,1477,1478,1488,1494],"ul",{},[47,1479,1480,1483,1484,1487],{},[51,1481,1482],{},"tracker.captureFromResponse(response)"," — call with a Cachely ",[51,1485,1486],{},"Response"," object to record the assignment.",[47,1489,1490,1493],{},[51,1491,1492],{},"tracker.wrapFetch(fetch)"," — wrap your own fetch implementation; identical to what the Prismic client does internally.",[47,1495,1496,155,1499,155,1502,1505],{},[51,1497,1498],{},"tracker.getAssignment()",[51,1500,1501],{},"tracker.getVisitorId()",[51,1503,1504],{},"tracker.getSessionId()"," — read the stored state.",[14,1507,1508],{},[17,1509,1510,1513],{},[20,1511,1512],{},"Do not"," issue a separate request whose only purpose is to capture experiment headers. The proxy assigns variants per request — a probe call may land on a different variant than the one the user actually saw, which attributes the click to the wrong arm. Always reuse the response that rendered the content.",[642,1515,1517],{"id":1516},"multiple-experiments-on-one-page","Multiple experiments on one page",[17,1519,1520,1521,1526],{},"The tracker stores assignments ",[20,1522,1523,1524],{},"keyed by ",[51,1525,430],{},", so a page running more than one Cachely experiment can attribute each CTA to its own experiment.",[1476,1528,1529,1542],{},[47,1530,1531,1534,1535,195,1538,1541],{},[20,1532,1533],{},"Single experiment"," — no extra work required. ",[51,1536,1537],{},"track('cta_click')",[51,1539,1540],{},"\u003Cbutton data-cachely-track=\"cta_click\">"," resolve automatically.",[47,1543,1544,1547,1548,1550,1551,1554],{},[20,1545,1546],{},"Multiple experiments"," — pass ",[51,1549,430],{}," explicitly. The SDK refuses to guess when more than one assignment is active and resolves with ",[51,1552,1553],{},"{ ok: false, error: 'ambiguous_experiment' }",". This prevents misattributing a click to the wrong arm.",[17,1556,1557],{},"Explicit form:",[233,1559,1561],{"className":870,"code":1560,"language":872,"meta":238,"style":238},"cachelyExperiments.track(\n  'homepage_hero_cta_click',\n  { cta: 'hero_primary' },\n  { experimentId: 1 },\n)\n",[51,1562,1563,1572,1579,1589,1598],{"__ignoreMap":238},[242,1564,1565,1567,1569],{"class":244,"line":245},[242,1566,1099],{"class":248},[242,1568,1102],{"class":681},[242,1570,1571],{"class":248},"(\n",[242,1573,1574,1577],{"class":244,"line":252},[242,1575,1576],{"class":262},"  'homepage_hero_cta_click'",[242,1578,266],{"class":248},[242,1580,1581,1584,1586],{"class":244,"line":269},[242,1582,1583],{"class":248},"  { cta: ",[242,1585,1119],{"class":262},[242,1587,1588],{"class":248}," },\n",[242,1590,1591,1594,1596],{"class":244,"line":282},[242,1592,1593],{"class":248},"  { experimentId: ",[242,1595,277],{"class":255},[242,1597,1588],{"class":248},[242,1599,1600],{"class":244,"line":295},[242,1601,1602],{"class":248},")\n",[17,1604,1605,1606,1608],{},"Declarative form (works with ",[51,1607,1152],{},"):",[233,1610,1612],{"className":1156,"code":1611,"language":1158,"meta":238,"style":238},"\u003Cbutton\n  data-cachely-track=\"homepage_hero_cta_click\"\n  data-cachely-experiment-id=\"1\"\n  data-cachely-meta-cta=\"hero_primary\"\n>\n  CTA\n\u003C\u002Fbutton>\n",[51,1613,1614,1620,1628,1638,1646,1650,1655],{"__ignoreMap":238},[242,1615,1616,1618],{"class":244,"line":245},[242,1617,1165],{"class":248},[242,1619,1207],{"class":1168},[242,1621,1622,1624,1626],{"class":244,"line":252},[242,1623,1212],{"class":681},[242,1625,1215],{"class":248},[242,1627,1218],{"class":262},[242,1629,1630,1633,1635],{"class":244,"line":269},[242,1631,1632],{"class":681},"  data-cachely-experiment-id",[242,1634,1215],{"class":248},[242,1636,1637],{"class":262},"\"1\"\n",[242,1639,1640,1642,1644],{"class":244,"line":282},[242,1641,1223],{"class":681},[242,1643,1215],{"class":248},[242,1645,1228],{"class":262},[242,1647,1648],{"class":244,"line":295},[242,1649,1172],{"class":248},[242,1651,1652],{"class":244,"line":308},[242,1653,1654],{"class":248},"  CTA\n",[242,1656,1657,1659,1661],{"class":244,"line":321},[242,1658,1192],{"class":248},[242,1660,1244],{"class":1168},[242,1662,1172],{"class":248},[17,1664,1665,1666,1669],{},"Resolution rules for ",[51,1667,1668],{},"track(name, meta, options)",":",[104,1671,1672,1687],{},[107,1673,1674],{},[110,1675,1676,1679,1684],{},[113,1677,1678],{},"Captured assignments",[113,1680,1681],{},[51,1682,1683],{},"options.experimentId",[113,1685,1686],{},"Result",[120,1688,1689,1701,1712,1725,1735,1747],{},[110,1690,1691,1694,1697],{},[125,1692,1693],{},"0",[125,1695,1696],{},"(any)",[125,1698,1699],{},[51,1700,1142],{},[110,1702,1703,1706,1709],{},[125,1704,1705],{},"≥ 1",[125,1707,1708],{},"not provided, but exactly 1 captured",[125,1710,1711],{},"uses that one (back-compat)",[110,1713,1714,1717,1720],{},[125,1715,1716],{},"≥ 2",[125,1718,1719],{},"not provided",[125,1721,1722,1724],{},[51,1723,1553],{}," (no POST)",[110,1726,1727,1729,1732],{},[125,1728,1696],{},[125,1730,1731],{},"provided + matches a captured assignment",[125,1733,1734],{},"uses the matching one",[110,1736,1737,1739,1742],{},[125,1738,1696],{},[125,1740,1741],{},"provided + no matching assignment",[125,1743,1744],{},[51,1745,1746],{},"{ ok: false, error: 'assignment_not_found' }",[110,1748,1749,1751,1754],{},[125,1750,1696],{},[125,1752,1753],{},"provided + non-positive \u002F non-numeric",[125,1755,1756],{},[51,1757,1758],{},"{ ok: false, error: 'invalid_experiment_id' }",[17,1760,1761,1762,1765,1766,1769],{},"Inspect the live state with ",[51,1763,1764],{},"tracker.getAllAssignments()"," (returns a plain ",[51,1767,1768],{},"Record\u003Cstring, CachelyExperimentAssignment>",").",[14,1771,1772],{},[17,1773,1774,1777,1778,1781],{},[20,1775,1776],{},"Follow-up:"," a future product improvement will give every experiment a stable string key so developers can write ",[51,1779,1780],{},"data-cachely-experiment=\"homepage_serbian_copy_test\""," instead of a numeric id. Until then, IDs are the contract.",[642,1783,1785],{"id":1784},"behaviour-notes","Behaviour notes",[1476,1787,1788,1809,1822,1828],{},[47,1789,1790,1793,1794,1796,1797,1799,1800,1802,1803,155,1806,1769],{},[20,1791,1792],{},"Source of truth."," The tracker uses ",[51,1795,149],{}," as ",[51,1798,168],{},". ",[51,1801,176],{}," is informational only — using it would break the moment a key diverges from its profile (e.g. ",[51,1804,1805],{},"key=serbian-v2",[51,1807,1808],{},"profile=serbian",[47,1810,1811,1814,1815,1796,1818,1821],{},[20,1812,1813],{},"Storage migration."," Trackers persist state under ",[51,1816,1817],{},"sessionStorage['cachely.experiment']",[51,1819,1820],{},"{ assignmentsById, latestExperimentId }",". Legacy single-assignment payloads from earlier SDK versions are read transparently and rewritten in the new shape on the next capture.",[47,1823,1824,1827],{},[20,1825,1826],{},"Storage failures degrade silently."," SSR, private mode, blocked third-party storage — the tracker falls back to in-memory state and never throws. Visitor and session ids are auto-generated when storage is unavailable.",[47,1829,1830,1835,1836,1839],{},[20,1831,1832],{},[51,1833,1834],{},"keepalive: true"," is preferred for ingest POSTs so events ride along across navigations. The tracker retries without ",[51,1837,1838],{},"keepalive"," if the runtime rejects it, so older browsers \u002F Node never block tracking.",[36,1841],{},[39,1843,1845],{"id":1844},"aggregation-endpoint","Aggregation endpoint",[17,1847,1848,1849,1855,1856,1859,1860,1863],{},"See ",[25,1850,1852],{"href":1851},".\u002Fai-transform#api-reference",[51,1853,1854],{},"GET \u002Fapi\u002Ftenants\u002F{slug}\u002Fai-experiments\u002F{id}\u002Fexposures"," in the AI Transform docs for the base contract. The endpoint now accepts an optional ",[51,1857,1858],{},"?goal=\u003CeventName>"," query (defaults to ",[51,1861,1862],{},"homepage_hero_cta_click",") and returns a strict superset of the previous shape — same authentication (Clerk session + tenant access, viewer+), same tenant-isolation guarantees, additional event-related fields per variant.",[17,1865,1866],{},"Response shape:",[233,1868,1870],{"className":235,"code":1869,"language":237,"meta":238,"style":238},"{\n  \"experimentId\": 1,\n  \"totalExposures\": 202,\n  \"totalEvents\": 22,\n  \"goalEventName\": \"homepage_hero_cta_click\",\n  \"variants\": [\n    {\n      \"variantId\": 1, \"variantKey\": \"control\", \"label\": \"Control\",\n      \"transformProfile\": null, \"trafficWeight\": 50,\n      \"exposures\": 100, \"actualSplit\": 49.5, \"expectedSplit\": 50.0,\n      \"lastSeenAt\": \"2026-05-12T10:01:02Z\",\n      \"eventCount\": 8, \"conversionRate\": 8.0,\n      \"lastEventAt\": \"2026-05-12T10:02:00Z\"\n    },\n    {\n      \"variantId\": 2, \"variantKey\": \"serbian\", \"label\": \"Serbian\",\n      \"transformProfile\": \"serbian\", \"trafficWeight\": 50,\n      \"exposures\": 102, \"actualSplit\": 50.5, \"expectedSplit\": 50.0,\n      \"lastSeenAt\": \"2026-05-12T10:01:05Z\",\n      \"eventCount\": 14, \"conversionRate\": 13.7,\n      \"lastEventAt\": \"2026-05-12T10:02:30Z\"\n    }\n  ]\n}\n",[51,1871,1872,1876,1886,1898,1910,1921,1929,1934,1965,1987,2019,2031,2053,2063,2068,2072,2099,2117,2145,2157,2178,2188,2194,2200],{"__ignoreMap":238},[242,1873,1874],{"class":244,"line":245},[242,1875,249],{"class":248},[242,1877,1878,1880,1882,1884],{"class":244,"line":252},[242,1879,272],{"class":255},[242,1881,259],{"class":248},[242,1883,277],{"class":255},[242,1885,266],{"class":248},[242,1887,1888,1891,1893,1896],{"class":244,"line":269},[242,1889,1890],{"class":255},"  \"totalExposures\"",[242,1892,259],{"class":248},[242,1894,1895],{"class":255},"202",[242,1897,266],{"class":248},[242,1899,1900,1903,1905,1908],{"class":244,"line":282},[242,1901,1902],{"class":255},"  \"totalEvents\"",[242,1904,259],{"class":248},[242,1906,1907],{"class":255},"22",[242,1909,266],{"class":248},[242,1911,1912,1915,1917,1919],{"class":244,"line":295},[242,1913,1914],{"class":255},"  \"goalEventName\"",[242,1916,259],{"class":248},[242,1918,316],{"class":262},[242,1920,266],{"class":248},[242,1922,1923,1926],{"class":244,"line":308},[242,1924,1925],{"class":255},"  \"variants\"",[242,1927,1928],{"class":248},": [\n",[242,1930,1931],{"class":244,"line":321},[242,1932,1933],{"class":248},"    {\n",[242,1935,1936,1939,1941,1943,1945,1948,1950,1953,1955,1958,1960,1963],{"class":244,"line":334},[242,1937,1938],{"class":255},"      \"variantId\"",[242,1940,259],{"class":248},[242,1942,277],{"class":255},[242,1944,155],{"class":248},[242,1946,1947],{"class":255},"\"variantKey\"",[242,1949,259],{"class":248},[242,1951,1952],{"class":262},"\"control\"",[242,1954,155],{"class":248},[242,1956,1957],{"class":255},"\"label\"",[242,1959,259],{"class":248},[242,1961,1962],{"class":262},"\"Control\"",[242,1964,266],{"class":248},[242,1966,1967,1970,1972,1975,1977,1980,1982,1985],{"class":244,"line":347},[242,1968,1969],{"class":255},"      \"transformProfile\"",[242,1971,259],{"class":248},[242,1973,1974],{"class":255},"null",[242,1976,155],{"class":248},[242,1978,1979],{"class":255},"\"trafficWeight\"",[242,1981,259],{"class":248},[242,1983,1984],{"class":255},"50",[242,1986,266],{"class":248},[242,1988,1989,1992,1994,1997,1999,2002,2004,2007,2009,2012,2014,2017],{"class":244,"line":360},[242,1990,1991],{"class":255},"      \"exposures\"",[242,1993,259],{"class":248},[242,1995,1996],{"class":255},"100",[242,1998,155],{"class":248},[242,2000,2001],{"class":255},"\"actualSplit\"",[242,2003,259],{"class":248},[242,2005,2006],{"class":255},"49.5",[242,2008,155],{"class":248},[242,2010,2011],{"class":255},"\"expectedSplit\"",[242,2013,259],{"class":248},[242,2015,2016],{"class":255},"50.0",[242,2018,266],{"class":248},[242,2020,2021,2024,2026,2029],{"class":244,"line":373},[242,2022,2023],{"class":255},"      \"lastSeenAt\"",[242,2025,259],{"class":248},[242,2027,2028],{"class":262},"\"2026-05-12T10:01:02Z\"",[242,2030,266],{"class":248},[242,2032,2033,2036,2038,2041,2043,2046,2048,2051],{"class":244,"line":393},[242,2034,2035],{"class":255},"      \"eventCount\"",[242,2037,259],{"class":248},[242,2039,2040],{"class":255},"8",[242,2042,155],{"class":248},[242,2044,2045],{"class":255},"\"conversionRate\"",[242,2047,259],{"class":248},[242,2049,2050],{"class":255},"8.0",[242,2052,266],{"class":248},[242,2054,2055,2058,2060],{"class":244,"line":825},[242,2056,2057],{"class":255},"      \"lastEventAt\"",[242,2059,259],{"class":248},[242,2061,2062],{"class":262},"\"2026-05-12T10:02:00Z\"\n",[242,2064,2065],{"class":244,"line":835},[242,2066,2067],{"class":248},"    },\n",[242,2069,2070],{"class":244,"line":841},[242,2071,1933],{"class":248},[242,2073,2074,2076,2078,2080,2082,2084,2086,2088,2090,2092,2094,2097],{"class":244,"line":847},[242,2075,1938],{"class":255},[242,2077,259],{"class":248},[242,2079,290],{"class":255},[242,2081,155],{"class":248},[242,2083,1947],{"class":255},[242,2085,259],{"class":248},[242,2087,303],{"class":262},[242,2089,155],{"class":248},[242,2091,1957],{"class":255},[242,2093,259],{"class":248},[242,2095,2096],{"class":262},"\"Serbian\"",[242,2098,266],{"class":248},[242,2100,2101,2103,2105,2107,2109,2111,2113,2115],{"class":244,"line":996},[242,2102,1969],{"class":255},[242,2104,259],{"class":248},[242,2106,303],{"class":262},[242,2108,155],{"class":248},[242,2110,1979],{"class":255},[242,2112,259],{"class":248},[242,2114,1984],{"class":255},[242,2116,266],{"class":248},[242,2118,2119,2121,2123,2126,2128,2130,2132,2135,2137,2139,2141,2143],{"class":244,"line":1001},[242,2120,1991],{"class":255},[242,2122,259],{"class":248},[242,2124,2125],{"class":255},"102",[242,2127,155],{"class":248},[242,2129,2001],{"class":255},[242,2131,259],{"class":248},[242,2133,2134],{"class":255},"50.5",[242,2136,155],{"class":248},[242,2138,2011],{"class":255},[242,2140,259],{"class":248},[242,2142,2016],{"class":255},[242,2144,266],{"class":248},[242,2146,2148,2150,2152,2155],{"class":244,"line":2147},19,[242,2149,2023],{"class":255},[242,2151,259],{"class":248},[242,2153,2154],{"class":262},"\"2026-05-12T10:01:05Z\"",[242,2156,266],{"class":248},[242,2158,2160,2162,2164,2167,2169,2171,2173,2176],{"class":244,"line":2159},20,[242,2161,2035],{"class":255},[242,2163,259],{"class":248},[242,2165,2166],{"class":255},"14",[242,2168,155],{"class":248},[242,2170,2045],{"class":255},[242,2172,259],{"class":248},[242,2174,2175],{"class":255},"13.7",[242,2177,266],{"class":248},[242,2179,2181,2183,2185],{"class":244,"line":2180},21,[242,2182,2057],{"class":255},[242,2184,259],{"class":248},[242,2186,2187],{"class":262},"\"2026-05-12T10:02:30Z\"\n",[242,2189,2191],{"class":244,"line":2190},22,[242,2192,2193],{"class":248},"    }\n",[242,2195,2197],{"class":244,"line":2196},23,[242,2198,2199],{"class":248},"  ]\n",[242,2201,2203],{"class":244,"line":2202},24,[242,2204,396],{"class":248},[17,2206,2207,2210,2211,2213,2214,2217],{},[51,2208,2209],{},"conversionRate"," is ",[51,2212,1693],{}," (not ",[51,2215,2216],{},"NaN",") for variants with zero exposures. Variants whose stored id\u002Fkey no longer match the current experiment definition (drift) are dropped from both exposures and events so the displayed splits always sum to ~100%.",[36,2219],{},[39,2221,2223],{"id":2222},"what-event-tracking-does-not-do","What event tracking does not do",[1476,2225,2226,2229,2238,2241],{},[47,2227,2228],{},"It is not a general analytics platform. The endpoint accepts one event per call and stores no derived metrics.",[47,2230,2231,2232,2234,2235,1769],{},"It does not deduplicate events. If your click handler fires twice you'll record two events; budget for that in ",[51,2233,502],{}," (e.g. include a client-generated ",[51,2236,2237],{},"eventId",[47,2239,2240],{},"It does not time-window aggregations yet — the dashboard view is all-time.",[47,2242,2243,2244,2246],{},"It does not block ",[51,2245,92],{}," traffic from submitting events, but those requests carry no experiment headers, so the snippet skips them automatically.",[2248,2249,2250],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}",{"title":238,"searchDepth":252,"depth":252,"links":2252},[2253,2254,2255,2261,2262],{"id":41,"depth":252,"text":42},{"id":98,"depth":252,"text":99},{"id":211,"depth":252,"text":212,"children":2256},[2257,2258,2259],{"id":219,"depth":269,"text":222},{"id":578,"depth":269,"text":581},{"id":609,"depth":269,"text":2260},"Recommended: @cachely-io\u002Fsdk integration",{"id":1844,"depth":252,"text":1845},{"id":2222,"depth":252,"text":2223},"Split traffic across AI transform variants, observe per-variant exposures, and track conversion events from your frontend to compute conversion rate per variant.","md",{},"\u002Fdocs\u002Fapi\u002Fai-experiments",{"title":5,"description":2263},"docs\u002Fapi\u002Fai-experiments","YmGNBUKxoY2PZITPV7W_5uh_vB_5zWIhWCgarbuCmYk",1781192375677]