[{"data":1,"prerenderedAt":1000},["ShallowReactive",2],{"docs-page:en:\u002Fdocs\u002Fquick-start\u002Fexpo":3,"docs-navigation:en":901},{"id":4,"title":5,"body":6,"description":894,"extension":895,"meta":896,"navigation":269,"path":897,"seo":898,"stem":899,"__hash__":900},"docs_en\u002Fen\u002Fdocs\u002Fquick-start\u002Fexpo.md","Expo quick start",{"type":7,"value":8,"toc":885},"minimark",[9,19,22,25,28,33,41,45,48,59,115,119,122,149,155,436,450,463,467,473,728,735,777,781,786,799,803,832,836,881],[10,11,12,13,18],"p",{},"Use this page to wire the installed Expo app. Publishing is a separate release step covered by ",[14,15,17],"a",{"href":16},"\u002Fdocs\u002Fquick-start\u002Fquick-start","Publish in 5 minutes",".",[10,20,21],{},"Otalan serves the authenticated update manifest and immutable asset URLs. Expo still owns the runtime download, install, and reload behavior.",[10,23,24],{},"Even with Expo asset caching, keep the exported update focused on the app shell. Do not import product images, videos, large fonts, PDFs, or marketing media into the JavaScript\u002FCSS bundle. Host content-heavy media on your external hosting and reference it by URL.",[10,26,27],{},"For Otalan Cloud, Otalan handles bundle validation for you.",[29,30,32],"h2",{"id":31},"native-build-prerequisites","Native build prerequisites",[10,34,35,36,40],{},"Expo OTA updates must be tested in a native build that includes ",[37,38,39],"code",{},"expo-updates",". For local Android builds, install Android Studio and the Android SDK. For local iOS builds, use macOS with Xcode. If you are not on a Mac, use EAS or another hosted build environment for iOS.",[29,42,44],{"id":43},"install-the-runtime-packages","Install the runtime packages",[10,46,47],{},"Run these commands in your Expo app repository:",[10,49,50,51,18],{},"NPM package: ",[14,52,56],{"href":53,"rel":54},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@otalan\u002Fexpo",[55],"nofollow",[37,57,58],{},"@otalan\u002Fexpo",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Install Expo update and application modules with Expo's version resolver.\nbunx expo install expo-updates expo-application\n# Install the Otalan Expo helper with Bun.\nbun add @otalan\u002Fexpo\n","bash","",[37,67,68,77,97,103],{"__ignoreMap":65},[69,70,73],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"sHwdD","# Install Expo update and application modules with Expo's version resolver.\n",[69,78,80,84,88,91,94],{"class":71,"line":79},2,[69,81,83],{"class":82},"sBMFI","bunx",[69,85,87],{"class":86},"sfazB"," expo",[69,89,90],{"class":86}," install",[69,92,93],{"class":86}," expo-updates",[69,95,96],{"class":86}," expo-application\n",[69,98,100],{"class":71,"line":99},3,[69,101,102],{"class":75},"# Install the Otalan Expo helper with Bun.\n",[69,104,106,109,112],{"class":71,"line":105},4,[69,107,108],{"class":82},"bun",[69,110,111],{"class":86}," add",[69,113,114],{"class":86}," @otalan\u002Fexpo\n",[29,116,118],{"id":117},"configure-expo-updates","Configure Expo updates",[10,120,121],{},"Create the app-side environment values. The OTA App Key is expected in the installed app; never put an OTA Publish Key in app code.",[60,123,127],{"className":124,"code":125,"language":126,"meta":65,"style":65},"language-dotenv shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","EXPO_PUBLIC_OTALAN_API_URL=https:\u002F\u002Fapi.otalan.com\nEXPO_PUBLIC_OTALAN_APP_KEY=otalan_ota_xxx\nEXPO_PUBLIC_OTALAN_APP_ID=com.example.app\nEXPO_PUBLIC_OTALAN_CHANNEL=production\n","dotenv",[37,128,129,134,139,144],{"__ignoreMap":65},[69,130,131],{"class":71,"line":72},[69,132,133],{},"EXPO_PUBLIC_OTALAN_API_URL=https:\u002F\u002Fapi.otalan.com\n",[69,135,136],{"class":71,"line":79},[69,137,138],{},"EXPO_PUBLIC_OTALAN_APP_KEY=otalan_ota_xxx\n",[69,140,141],{"class":71,"line":99},[69,142,143],{},"EXPO_PUBLIC_OTALAN_APP_ID=com.example.app\n",[69,145,146],{"class":71,"line":105},[69,147,148],{},"EXPO_PUBLIC_OTALAN_CHANNEL=production\n",[10,150,151,152,18],{},"Add the Otalan update endpoint to your Expo config. Keep the OTA App Key in ",[37,153,154],{},"updates.requestHeaders",[60,156,160],{"className":157,"code":158,"language":159,"meta":65,"style":65},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F app.config.ts\nconst apiUrl = process.env.EXPO_PUBLIC_OTALAN_API_URL!\nconst apiKey = process.env.EXPO_PUBLIC_OTALAN_APP_KEY!\nconst appId = process.env.EXPO_PUBLIC_OTALAN_APP_ID!\nconst channel = process.env.EXPO_PUBLIC_OTALAN_CHANNEL!\n\nexport default {\n  expo: {\n    runtimeVersion: '1.0.0',\n    updates: {\n      url: `${apiUrl}\u002Fexpo\u002Fupdates?appId=${appId}&channel=${channel}`,\n      requestHeaders: {\n        'x-api-key': apiKey,\n      },\n      checkAutomatically: 'NEVER',\n    },\n  },\n}\n","ts",[37,161,162,167,197,219,241,264,271,284,296,316,326,367,377,395,401,418,424,430],{"__ignoreMap":65},[69,163,164],{"class":71,"line":72},[69,165,166],{"class":75},"\u002F\u002F app.config.ts\n",[69,168,169,173,177,181,184,186,189,191,194],{"class":71,"line":79},[69,170,172],{"class":171},"spNyl","const",[69,174,176],{"class":175},"sTEyZ"," apiUrl ",[69,178,180],{"class":179},"sMK4o","=",[69,182,183],{"class":175}," process",[69,185,18],{"class":179},[69,187,188],{"class":175},"env",[69,190,18],{"class":179},[69,192,193],{"class":175},"EXPO_PUBLIC_OTALAN_API_URL",[69,195,196],{"class":179},"!\n",[69,198,199,201,204,206,208,210,212,214,217],{"class":71,"line":99},[69,200,172],{"class":171},[69,202,203],{"class":175}," apiKey ",[69,205,180],{"class":179},[69,207,183],{"class":175},[69,209,18],{"class":179},[69,211,188],{"class":175},[69,213,18],{"class":179},[69,215,216],{"class":175},"EXPO_PUBLIC_OTALAN_APP_KEY",[69,218,196],{"class":179},[69,220,221,223,226,228,230,232,234,236,239],{"class":71,"line":105},[69,222,172],{"class":171},[69,224,225],{"class":175}," appId ",[69,227,180],{"class":179},[69,229,183],{"class":175},[69,231,18],{"class":179},[69,233,188],{"class":175},[69,235,18],{"class":179},[69,237,238],{"class":175},"EXPO_PUBLIC_OTALAN_APP_ID",[69,240,196],{"class":179},[69,242,244,246,249,251,253,255,257,259,262],{"class":71,"line":243},5,[69,245,172],{"class":171},[69,247,248],{"class":175}," channel ",[69,250,180],{"class":179},[69,252,183],{"class":175},[69,254,18],{"class":179},[69,256,188],{"class":175},[69,258,18],{"class":179},[69,260,261],{"class":175},"EXPO_PUBLIC_OTALAN_CHANNEL",[69,263,196],{"class":179},[69,265,267],{"class":71,"line":266},6,[69,268,270],{"emptyLinePlaceholder":269},true,"\n",[69,272,274,278,281],{"class":71,"line":273},7,[69,275,277],{"class":276},"s7zQu","export",[69,279,280],{"class":276}," default",[69,282,283],{"class":179}," {\n",[69,285,287,291,294],{"class":71,"line":286},8,[69,288,290],{"class":289},"swJcz","  expo",[69,292,293],{"class":179},":",[69,295,283],{"class":179},[69,297,299,302,304,307,310,313],{"class":71,"line":298},9,[69,300,301],{"class":289},"    runtimeVersion",[69,303,293],{"class":179},[69,305,306],{"class":179}," '",[69,308,309],{"class":86},"1.0.0",[69,311,312],{"class":179},"'",[69,314,315],{"class":179},",\n",[69,317,319,322,324],{"class":71,"line":318},10,[69,320,321],{"class":289},"    updates",[69,323,293],{"class":179},[69,325,283],{"class":179},[69,327,329,332,334,337,340,343,346,349,352,354,357,359,362,365],{"class":71,"line":328},11,[69,330,331],{"class":289},"      url",[69,333,293],{"class":179},[69,335,336],{"class":179}," `${",[69,338,339],{"class":175},"apiUrl",[69,341,342],{"class":179},"}",[69,344,345],{"class":86},"\u002Fexpo\u002Fupdates?appId=",[69,347,348],{"class":179},"${",[69,350,351],{"class":175},"appId",[69,353,342],{"class":179},[69,355,356],{"class":86},"&channel=",[69,358,348],{"class":179},[69,360,361],{"class":175},"channel",[69,363,364],{"class":179},"}`",[69,366,315],{"class":179},[69,368,370,373,375],{"class":71,"line":369},12,[69,371,372],{"class":289},"      requestHeaders",[69,374,293],{"class":179},[69,376,283],{"class":179},[69,378,380,383,386,388,390,393],{"class":71,"line":379},13,[69,381,382],{"class":179},"        '",[69,384,385],{"class":289},"x-api-key",[69,387,312],{"class":179},[69,389,293],{"class":179},[69,391,392],{"class":175}," apiKey",[69,394,315],{"class":179},[69,396,398],{"class":71,"line":397},14,[69,399,400],{"class":179},"      },\n",[69,402,404,407,409,411,414,416],{"class":71,"line":403},15,[69,405,406],{"class":289},"      checkAutomatically",[69,408,293],{"class":179},[69,410,306],{"class":179},[69,412,413],{"class":86},"NEVER",[69,415,312],{"class":179},[69,417,315],{"class":179},[69,419,421],{"class":71,"line":420},16,[69,422,423],{"class":179},"    },\n",[69,425,427],{"class":71,"line":426},17,[69,428,429],{"class":179},"  },\n",[69,431,433],{"class":71,"line":432},18,[69,434,435],{"class":179},"}\n",[10,437,438,441,442,445,446,449],{},[37,439,440],{},"checkAutomatically: 'NEVER'"," keeps update checks under your code so ",[37,443,444],{},"updater.check()"," or ",[37,447,448],{},"updater.sync()"," controls when Expo contacts Otalan.",[10,451,452,454,455,458,459,462],{},[37,453,58],{}," creates the stable rollout device ID and writes ",[37,456,457],{},"otalan-device-id"," to Expo update extra params before checking. Do not add a separate ",[37,460,461],{},"x-device-id"," header in app code.",[29,464,466],{"id":465},"add-an-update-check","Add an update check",[10,468,469,470,293],{},"Create ",[37,471,472],{},"src\u002Fota-update.ts",[60,474,476],{"className":157,"code":475,"language":159,"meta":65,"style":65},"import { initializeUpdater, type InitializedExpoUpdater } from '@otalan\u002Fexpo'\n\nlet updater: InitializedExpoUpdater | undefined\n\nexport async function syncOtalanUpdates() {\n  if (!updater) {\n    updater = await initializeUpdater({\n      apiUrl: process.env.EXPO_PUBLIC_OTALAN_API_URL!,\n      apiKey: process.env.EXPO_PUBLIC_OTALAN_APP_KEY!,\n      appId: process.env.EXPO_PUBLIC_OTALAN_APP_ID!,\n      channel: process.env.EXPO_PUBLIC_OTALAN_CHANNEL!,\n    })\n  }\n\n  \u002F\u002F If you only want to check availability without updating yet, call check() and inspect updateAvailable.\n  \u002F\u002F const { updateAvailable } = await updater.check()\n\n  \u002F\u002F sync() checks too, then fetches and reloads when an update is available.\n  return updater.sync()\n}\n",[37,477,478,511,515,533,537,556,576,594,614,633,652,671,679,684,688,693,698,702,707,723],{"__ignoreMap":65},[69,479,480,483,486,489,492,495,498,501,504,506,508],{"class":71,"line":72},[69,481,482],{"class":276},"import",[69,484,485],{"class":179}," {",[69,487,488],{"class":175}," initializeUpdater",[69,490,491],{"class":179},",",[69,493,494],{"class":276}," type",[69,496,497],{"class":175}," InitializedExpoUpdater",[69,499,500],{"class":179}," }",[69,502,503],{"class":276}," from",[69,505,306],{"class":179},[69,507,58],{"class":86},[69,509,510],{"class":179},"'\n",[69,512,513],{"class":71,"line":79},[69,514,270],{"emptyLinePlaceholder":269},[69,516,517,520,523,525,527,530],{"class":71,"line":99},[69,518,519],{"class":171},"let",[69,521,522],{"class":175}," updater",[69,524,293],{"class":179},[69,526,497],{"class":82},[69,528,529],{"class":179}," |",[69,531,532],{"class":82}," undefined\n",[69,534,535],{"class":71,"line":105},[69,536,270],{"emptyLinePlaceholder":269},[69,538,539,541,544,547,551,554],{"class":71,"line":243},[69,540,277],{"class":276},[69,542,543],{"class":171}," async",[69,545,546],{"class":171}," function",[69,548,550],{"class":549},"s2Zo4"," syncOtalanUpdates",[69,552,553],{"class":179},"()",[69,555,283],{"class":179},[69,557,558,561,564,567,570,573],{"class":71,"line":266},[69,559,560],{"class":276},"  if",[69,562,563],{"class":289}," (",[69,565,566],{"class":179},"!",[69,568,569],{"class":175},"updater",[69,571,572],{"class":289},") ",[69,574,575],{"class":179},"{\n",[69,577,578,581,584,587,589,592],{"class":71,"line":273},[69,579,580],{"class":175},"    updater",[69,582,583],{"class":179}," =",[69,585,586],{"class":276}," await",[69,588,488],{"class":549},[69,590,591],{"class":289},"(",[69,593,575],{"class":179},[69,595,596,599,601,603,605,607,609,611],{"class":71,"line":286},[69,597,598],{"class":289},"      apiUrl",[69,600,293],{"class":179},[69,602,183],{"class":175},[69,604,18],{"class":179},[69,606,188],{"class":175},[69,608,18],{"class":179},[69,610,193],{"class":175},[69,612,613],{"class":179},"!,\n",[69,615,616,619,621,623,625,627,629,631],{"class":71,"line":298},[69,617,618],{"class":289},"      apiKey",[69,620,293],{"class":179},[69,622,183],{"class":175},[69,624,18],{"class":179},[69,626,188],{"class":175},[69,628,18],{"class":179},[69,630,216],{"class":175},[69,632,613],{"class":179},[69,634,635,638,640,642,644,646,648,650],{"class":71,"line":318},[69,636,637],{"class":289},"      appId",[69,639,293],{"class":179},[69,641,183],{"class":175},[69,643,18],{"class":179},[69,645,188],{"class":175},[69,647,18],{"class":179},[69,649,238],{"class":175},[69,651,613],{"class":179},[69,653,654,657,659,661,663,665,667,669],{"class":71,"line":328},[69,655,656],{"class":289},"      channel",[69,658,293],{"class":179},[69,660,183],{"class":175},[69,662,18],{"class":179},[69,664,188],{"class":175},[69,666,18],{"class":179},[69,668,261],{"class":175},[69,670,613],{"class":179},[69,672,673,676],{"class":71,"line":369},[69,674,675],{"class":179},"    }",[69,677,678],{"class":289},")\n",[69,680,681],{"class":71,"line":379},[69,682,683],{"class":179},"  }\n",[69,685,686],{"class":71,"line":397},[69,687,270],{"emptyLinePlaceholder":269},[69,689,690],{"class":71,"line":403},[69,691,692],{"class":75},"  \u002F\u002F If you only want to check availability without updating yet, call check() and inspect updateAvailable.\n",[69,694,695],{"class":71,"line":420},[69,696,697],{"class":75},"  \u002F\u002F const { updateAvailable } = await updater.check()\n",[69,699,700],{"class":71,"line":426},[69,701,270],{"emptyLinePlaceholder":269},[69,703,704],{"class":71,"line":432},[69,705,706],{"class":75},"  \u002F\u002F sync() checks too, then fetches and reloads when an update is available.\n",[69,708,710,713,715,717,720],{"class":71,"line":709},19,[69,711,712],{"class":276},"  return",[69,714,522],{"class":175},[69,716,18],{"class":179},[69,718,719],{"class":549},"sync",[69,721,722],{"class":289},"()\n",[69,724,726],{"class":71,"line":725},20,[69,727,435],{"class":179},[10,729,730,731,734],{},"Call ",[37,732,733],{},"syncOtalanUpdates()"," from app boot, a settings screen, or any user interaction that should check for updates:",[60,736,738],{"className":157,"code":737,"language":159,"meta":65,"style":65},"\u002F\u002F src\u002Fmain.ts\nimport { syncOtalanUpdates } from '.\u002Fota-update'\n\nvoid syncOtalanUpdates()\n",[37,739,740,745,764,768],{"__ignoreMap":65},[69,741,742],{"class":71,"line":72},[69,743,744],{"class":75},"\u002F\u002F src\u002Fmain.ts\n",[69,746,747,749,751,753,755,757,759,762],{"class":71,"line":79},[69,748,482],{"class":276},[69,750,485],{"class":179},[69,752,550],{"class":175},[69,754,500],{"class":179},[69,756,503],{"class":276},[69,758,306],{"class":179},[69,760,761],{"class":86},".\u002Fota-update",[69,763,510],{"class":179},[69,765,766],{"class":71,"line":99},[69,767,270],{"emptyLinePlaceholder":269},[69,769,770,773,775],{"class":71,"line":105},[69,771,772],{"class":179},"void",[69,774,550],{"class":549},[69,776,722],{"class":175},[29,778,780],{"id":779},"publish-the-first-bundle","Publish the first bundle",[10,782,783,784,18],{},"After the native build contains this Expo update configuration, publish a baseline bundle from the app repository with ",[14,785,17],{"href":16},[10,787,788,789,791,792,794,795,798],{},"Use the same ",[37,790,351],{},", ",[37,793,361],{},", and ",[37,796,797],{},"runtimeVersion"," values that the installed Expo app reports.",[29,800,802],{"id":801},"verify-the-update","Verify the update",[804,805,806,810,820,826,829],"ol",{},[807,808,809],"li",{},"Install a native build that contains the Expo config above.",[807,811,812,813,816,817,18],{},"Publish a visible text or style change through ",[37,814,815],{},"otalan bundle"," and ",[37,818,819],{},"otalan publish",[807,821,822,823,825],{},"Run the update-check code from ",[37,824,472],{}," on the device.",[807,827,828],{},"Confirm the app reloads into the new web assets.",[807,830,831],{},"Roll back to the first bundle from the dashboard and trigger another check.",[29,833,835],{"id":834},"troubleshooting","Troubleshooting",[837,838,839,849,861,872],"ul",{},[807,840,841,842,791,844,791,846,848],{},"If no update is available, check the ",[37,843,351],{},[37,845,361],{},[37,847,797],{},", OTA App Key, and device rollout assignment.",[807,850,851,852,855,856,858,859,18],{},"If the manifest request is rejected, confirm that ",[37,853,854],{},"expo-application"," is installed and the native ",[37,857,154],{}," contains ",[37,860,385],{},[807,862,863,864,867,868,871],{},"If you use a local Otalan API URL, make sure the native runtime can reach it. Physical devices usually need your machine's LAN IP, Android emulators often need ",[37,865,866],{},"10.0.2.2",", and Android HTTP testing may require ",[37,869,870],{},"android:usesCleartextTraffic=\"true\""," or the equivalent Expo Android config in development only.",[807,873,874,875,877,878,880],{},"If Expo downloads but does not reload, inspect the ",[37,876,448],{}," result and test with a native build that contains the ",[37,879,39],{}," config.",[882,883,884],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":65,"searchDepth":99,"depth":99,"links":886},[887,888,889,890,891,892,893],{"id":31,"depth":79,"text":32},{"id":43,"depth":79,"text":44},{"id":117,"depth":79,"text":118},{"id":465,"depth":79,"text":466},{"id":779,"depth":79,"text":780},{"id":801,"depth":79,"text":802},{"id":834,"depth":79,"text":835},"Configure expo-updates with Otalan, add the Otalan Expo helper, and make the installed app ready to receive compatible OTA bundles.","md",{},"\u002Fen\u002Fdocs\u002Fquick-start\u002Fexpo",{"title":5,"description":894},"en\u002Fdocs\u002Fquick-start\u002Fexpo","tEB4y_iaMTKN51Rqnax6VcZUmIYF5xvX-vqYBrBhiD4",[902],{"title":903,"path":904,"stem":905,"children":906,"page":924},"En","\u002Fen","en",[907],{"title":908,"path":909,"stem":910,"children":911,"page":-1,"description":913},"Introduction","\u002Fen\u002Fdocs","en\u002Fdocs\u002Findex",[912,914,925,935,945,955,970,990],{"title":908,"path":909,"stem":910,"description":913},"Understand what Otalan is, when to use it, and how the first safe OTA release flow works for Capacitor and Expo apps.",{"title":915,"path":916,"stem":917,"children":918,"page":924},"About","\u002Fen\u002Fdocs\u002Fabout","en\u002Fdocs\u002Fabout",[919],{"title":920,"path":921,"stem":922,"description":923},"Security and trust","\u002Fen\u002Fdocs\u002Fabout\u002Fsecurity-trust","en\u002Fdocs\u002Fabout\u002Fsecurity-trust","How Otalan keeps OTA releases controlled: scoped keys, bundle validation, exact matching, rollout controls, rollback, deletion behavior, and support contact.",false,{"title":926,"path":927,"stem":928,"children":929,"page":924},"Build","\u002Fen\u002Fdocs\u002Fbuild","en\u002Fdocs\u002Fbuild",[930],{"title":931,"path":932,"stem":933,"description":934},"Generate a bundle","\u002Fen\u002Fdocs\u002Fbuild\u002Fgenerate-bundle","en\u002Fdocs\u002Fbuild\u002Fgenerate-bundle","Prepare a release artifact that will pass Otalan's validation pipeline, including the extra considerations required for Expo-based publish flows.",{"title":936,"path":937,"stem":938,"children":939,"page":924},"Deploy","\u002Fen\u002Fdocs\u002Fdeploy","en\u002Fdocs\u002Fdeploy",[940],{"title":941,"path":942,"stem":943,"description":944},"Publish a release","\u002Fen\u002Fdocs\u002Fdeploy\u002Fpublish-release","en\u002Fdocs\u002Fdeploy\u002Fpublish-release","Submit a release through the dashboard, understand each field in the publish form, and operate the validation lifecycle with confidence.",{"title":946,"path":947,"stem":948,"children":949,"page":924},"Integration","\u002Fen\u002Fdocs\u002Fintegration","en\u002Fdocs\u002Fintegration",[950],{"title":951,"path":952,"stem":953,"description":954},"Migrate from App Center CodePush","\u002Fen\u002Fdocs\u002Fintegration\u002Fapp-center-codepush","en\u002Fdocs\u002Fintegration\u002Fapp-center-codepush","Plan a migration from Microsoft App Center CodePush to Otalan, with mapping notes, rollout guidance, and common failure modes.",{"title":956,"path":957,"stem":958,"children":959,"page":924},"Quick Start","\u002Fen\u002Fdocs\u002Fquick-start","en\u002Fdocs\u002Fquick-start",[960,965,966],{"title":961,"path":962,"stem":963,"description":964},"Capacitor quick start","\u002Fen\u002Fdocs\u002Fquick-start\u002Fcapacitor","en\u002Fdocs\u002Fquick-start\u002Fcapacitor","Wire the Otalan Capacitor runtime into an installed app so it can check for OTA updates, install compatible bundles, and confirm successful launches.",{"title":5,"path":897,"stem":899,"description":894},{"title":17,"path":967,"stem":968,"description":969},"\u002Fen\u002Fdocs\u002Fquick-start\u002Fquick-start","en\u002Fdocs\u002Fquick-start\u002Fquick-start","Create the first Otalan release path, publish a baseline bundle, verify one update, and prove rollback before widening rollout.",{"title":971,"path":972,"stem":973,"children":974,"page":924},"Tooling","\u002Fen\u002Fdocs\u002Ftooling","en\u002Fdocs\u002Ftooling",[975,980,985],{"title":976,"path":977,"stem":978,"description":979},"AI skill","\u002Fen\u002Fdocs\u002Ftooling\u002Fai-skill","en\u002Fdocs\u002Ftooling\u002Fai-skill","Copy a compact assistant skill for Otalan SDK setup, CLI publishing, and safe credential boundaries.",{"title":981,"path":982,"stem":983,"description":984},"CLI","\u002Fen\u002Fdocs\u002Ftooling\u002Fcli","en\u002Fdocs\u002Ftooling\u002Fcli","Learn when to use the Otalan CLI, what workflows it covers, and how it complements the dashboard instead of replacing the platform model.",{"title":986,"path":987,"stem":988,"description":989},"SDKs","\u002Fen\u002Fdocs\u002Ftooling\u002Fsdk","en\u002Fdocs\u002Ftooling\u002Fsdk","Understand the difference between the Capacitor and Expo integrations, and choose the runtime package that matches the way your mobile app actually updates.",{"title":991,"path":992,"stem":993,"children":994,"page":924},"Versions","\u002Fen\u002Fdocs\u002Fversions","en\u002Fdocs\u002Fversions",[995],{"title":996,"path":997,"stem":998,"description":999},"v1","\u002Fen\u002Fdocs\u002Fversions\u002Fv1","en\u002Fdocs\u002Fversions\u002Fv1","Supported runtimes, public limits, and post-v1 candidates for Otalan v1.",1780287525123]