← Back to the package
Embed Migration Runbook

Embed Migration Runbook

Move one merchant store from script tags and legacy theme code to a theme app embed, and prove it works before you remove anything. This runbook is for any developer or support engineer. You don't need to know the kit's code.

Prepared by David · October 2026 · Embed Migration Kit

1. Who this is for and what you get

Who: a developer or support engineer who migrates merchant stores one at a time. You follow the steps, read what the app shows, and you only call the lead developer in the cases listed in section 7.

At the end of one store you have:

How long it takes: the kit's own work for one store takes minutes. Most of the time is waiting for the merchant: the activation click (Stage 2) and the written approval for theme cleanup (Stage 4). The full flow has run end to end on one development store (October 10, 2026). Real merchant timings are not measured yet: write yours in the report.

Why now: three dates, not two.

DateWhat happens
October 1, 2026 (passed)Shopify rejects new script tags on every API version: "This app can't create or update script tags. Use theme app extensions instead."
January 1, 2027The Admin API rejects non-expiring offline access tokens.
March 1, 2027Shopify stops injecting script tags.

2. Before you start

Check every item before you open the app.

  1. Access to the store. Use a collaborator account, not a shared login. Any theme cleanup is done by a person, on a duplicated theme, with written approval.
  2. The app is installed on the store, and the app version with the embed is released. A new app version takes several minutes to reach stores.
  3. Scopes. The app needs read_script_tags, write_script_tags and read_themes. For prices by country it also needs read_products, write_products and read_markets. It never asks for write_themes. Keep write_script_tags until the audit reads zero script tags in every reachable store. One developer had 107 stores that re-authorized without that scope, and their tags could no longer be deleted.
  4. Markets. The country selector appears only when the store has at least two markets (Settings → Markets). With one market the embed still runs and reports, but there is nothing to choose.
  5. Written consent from the merchant before any theme cleanup or variant removal. The kit never deletes theme code, variants or products.
  6. A duplicated, unpublished copy of the live theme, for any cleanup.

3. The five stages for one store

Open the store admin → Apps → Embed Migration Kit. At the top you see a banner "Store status: …" and, on the right, the button Re-scan store. Click Re-scan store after every change you make outside the app.

StatusWhat it means
Still registeredNot verified yet: at least one of the three signals is missing.
ActiveVerified. Script tags or legacy theme markers still exist.
RemovedVerified, with 0 script tags and 0 legacy markers in the live theme.
The status banner after verification: embed enabled, beacon received, legacy markers still in the theme
The status banner after verification: embed enabled, beacon received, legacy markers still in the theme

Stage 1 · Measure

Goal: know everything the old integration left behind in this store.

Do:

  1. Go to the section 1 · Measure.
  2. Read the three numbers: script tags registered, legacy markers in the live theme and themes scanned.
  3. Read the table under each theme that has markers: File (file and line), What and Code.
  4. Optional, for the merchant or your lead: click Ask Shopify to create a script tag (live proof).

You should see:

The kit finds these legacy markers:

What the table saysWhat it means
Price wrapped in <span class="money"> for JS conversionA price that old JavaScript rewrites in the browser.
Old currency <select id="currencies">The old currency picker.
Old .currency-picker widgetThe old picker widget.
Theme includes a legacy currency snippetA {% render %} or {% include %} of the old snippet.
Client-side currency conversion libraryOld conversion code (for example Currency.convertAll).

The Script Tag API only returns this app's own script tags. The kit can't see or touch tags from other apps.

Stop if: the live proof shows "Shopify created the script tag". That means the platform behaves differently from what this runbook expects. Escalate (section 7).

Stage 1 · Measure: zero script tags, six legacy markers in the live theme, each one with file and line
Stage 1 · Measure: zero script tags, six legacy markers in the live theme, each one with file and line

Stage 2 · Activate

Goal: the embed is switched on in the live theme.

An app can't switch an embed on by itself. A person must click it in the theme editor, normally the merchant.

Do:

  1. Go to the section 2 · Activate. The table has one row per theme: Theme, Role (Live or Draft), Embed (settings_data.json), Legacy markers, Last beacon and Action.
  2. On the Live row, click Activate embed. It opens the theme editor on that exact theme, by its ID, with the embed ready to switch on. If the merchant does this step, send them Template A (section 4) with this link.
  3. In the theme editor, under App embeds, find Currency switcher (Embed Migration Kit) and switch it on.
  4. Leave the three settings like this: Position (Bottom left or Bottom right, as the merchant prefers), Neutralize legacy currency code left in the theme: on, Report to the app that the embed is running: on.
  5. Click Save.

Manual path (if the link doesn't work): Online Store → Themes → Customize → App embeds → "Currency switcher" → On → Save.

You should see: back in the app, after Re-scan store, the Live row shows Enabled and its button changes to Open in editor. The other embed states are Disabled, Not added yet (never switched on) and Unreadable.

Stop if: the editor opens a theme that is not the live one. Close it and use the manual path on the live theme. The kit builds the link by theme ID because the official themes/current link has opened the draft theme after a theme switch.

Remember: the embed lives per theme. If the merchant publishes another theme later, the new theme starts without the embed. The per-theme table shows it: activate again on the new live theme.

Stage 2 · Activate: the theme editor (admin in Spanish) with Currency switcher on and its three settings
Stage 2 · Activate: the theme editor (admin in Spanish) with Currency switcher on and its three settings

Stage 3 · Verify

Goal: prove that the embed is running, not only enabled. "Enabled isn't running."

SignalWhere it comes fromGreen when
settings_data.json on the live themeThe app reads config/settings_data.json of the live theme.Enabled
app.extensions() in the adminRead only while someone has the app open in the admin, then stored with a date.active. "Not read yet" does not block verification.
Beacon from the storefrontThe embed calls the app through the app proxy when it runs on a storefront page.Received in the last 72 hours

Do:

  1. Open the storefront once, any page. This sends the beacon. If the store has a password page, enter the password.
  2. Look at the storefront: the Ship to selector appears in the corner the merchant chose, and the old currency pickers are hidden.
  3. Go back to the app, click Re-scan store and read the section 3 · Verify.

You should see:

Stop if: you see "No recent beacon from the storefront" after visiting the storefront. Go to the troubleshooting table (section 5).

Stage 3 · The storefront with the Ship to selector from the embed (bottom left), set to Mexico · MXN
Stage 3 · The storefront with the Ship to selector from the embed (bottom left), set to Mexico · MXN

Stage 4 · Remove

Goal: delete this app's script tags, one at a time, only after verification. Then clean the theme by hand, with approval.

Do:

  1. Go to the section 4 · Remove.
  2. Click Queue script tag removal. The button works only when the store is verified and has script tags. If the store is not verified you see "Removal stays locked until all three signals are green."
  3. Click Run queue now. Each click processes up to 10 jobs that are due. Read the table: Script tag, Status, Attempts, Note.
  4. If a job shows retry, click Run queue now again later. The app waits on its own before it tries again (see the table below).
  5. Read the Theme cleanup checklist. Each line says the file, the line, what it is, and either "Already neutralized by the embed on the storefront." or "Not neutralized yet: activate the embed first."
  6. Send Template B (section 4) to ask for written approval. Clean only on a duplicated, unpublished theme, then the merchant publishes it.
Queue statusWhat it meansWhat you do
doneDeleted. "Script tag does not exist" also counts as done.Nothing.
retryThrottled, 423 locked (usually temporary), a server error or a network error. A 423 waits 15 minutes and doubles each time, up to 6 hours.Click Run queue now later. Never mark a 423 store as lost.
unreachable402 frozen store, 404 store closed or app uninstalled, 401/403 token rejected, or a missing scope.Put it on the unreachable list with its note (see section 5).
failedAny other error.Escalate (section 7).

Don't: try to remove anything before the three signals are green. The app refuses it: "The embed is not verified on the live theme yet."

After March 1, 2027, script tags that are left will have no effect. Delete them anyway when you can: they still count as usage.

Stage 5 · Prices by country

Do this stage only if the store fakes a local price with one variant per country (an option such as "Country" with United States, Mexico…). Under Markets, that old variant is converted again, so the buyer sees a wrong price.

Do:

  1. Go to the section 5 · Prices by country. The first table lists the products with one variant per country: Product, Option, Countries, Stock split by country, Variants.
  2. If you see "No active market with its own currency for: …", ask the merchant to create that market in Settings → Markets first.
  3. For each market, read the plan: the market, its catalog, the price list in the market's currency, the number of fixed prices (sent in batches of up to 250) and the audit.
  4. Read the second table: Variant that stays, Legacy variant, Fixed price to set, Shopify charges in … and Audit.
  5. Click Migrate prices (market name). Wait for the banner "Prices migrated for …" with its log.

You should see: the Audit column shows "✓ fixed price applied" for every row, and the last log line says the audit matches, for example 1/1 match.

Example from the development store: The Videographer Snowboard in Mexico went from 16,558.00 MXN (885.95 USD converted) to a fixed 15,999.00 MXN. The old "Mexico" variant alone would show 299,006 MXN, because Markets converts the old local price again.

Safe to repeat: the button changes to Run the … migration again. It reuses the catalog and the price list.

Left for the merchant (the kit never deletes variants or products): check open orders, remove the legacy variant, move its stock back to the variant that stays, and, when no legacy variants are left, remove the "Country" option.

Limits: charging in the local currency needs Shopify Payments or Adyen. Rounding works only with Shopify Payments.

Stage 5 · The product with the store set to Mexico: the fixed price of 15,999.00 MXN. The legacy "Country" option is still there: removing it is left for the merchant
Stage 5 · The product with the store set to Mexico: the fixed price of 15,999.00 MXN. The legacy "Country" option is still there: removing it is left for the merchant

Tokens · January 1, 2027

The card Tokens · January 1, 2027 shows one of two banners:

Report

In the section Report, click Download JSON and Download CSV. The files are named migration-<store>.json and migration-<store>.csv. See section 9.

4. Messages to the merchant

Replace the text in [brackets]. Keep the messages short.

Template A · Ask for the activation click

Subject: One click to keep your currency selector working after March 1, 2027

Hi [name],

Shopify is retiring the old way apps load on storefronts. From March 1, 2027 it stops loading "script tags", and the currency selector on [store] still uses the old method.

We moved the selector to Shopify's new method, called an "app embed". Shopify only lets the store owner switch it on, so we need one click from you:

  1. Open this link while you are logged in to your Shopify admin: [link from "Activate embed"]
  2. Make sure "Currency switcher" is on.
  3. Click Save.

That's all. We check your storefront afterwards and confirm that everything works before we remove anything old.

Thank you,
[your name]

Template B · Ask for approval to clean the theme

Subject: Approval to remove old currency code from your theme

Hi [name],

The new currency selector is running on [store], and it already hides the old code on your storefront. Your theme still has [number] old lines of code from the previous setup:

[paste the "Theme cleanup checklist": file, line, what it is]

We would like to remove them. This is how we work:

  1. We make a copy of your live theme and clean only the copy.
  2. We send you a preview link so you can check it.
  3. You publish the clean copy when you are happy with it. Your current theme stays as a backup.

Please reply "Approved" to this email so we can start. Nothing changes on your live theme without your approval.

Thank you,
[your name]

5. Troubleshooting

SymptomLikely causeFix
The theme editor opens a draft theme.The official themes/current link has opened the draft theme after a theme switch. The deep link has also failed for all apps for about a day before.Use Activate embed on the Live row (it uses the theme ID) or the manual path on the live theme.
Embed Enabled, but "No recent beacon from the storefront".Nobody opened the storefront in the last 72 hours.Open any storefront page, then Re-scan store.
Same, after visiting the storefront."Report to the app that the embed is running" is off.Switch it on in the theme editor, Save, visit the storefront again.
Same, and the setting is on.Embeds with target: body are inserted before the first literal </body>. If the theme has </body> inside a comment, the embed lands inside the comment and silently never runs.Open the browser's developer tools (Elements) on the storefront and check that the <html> tag has data-emk="running". If it is missing, escalate (theme structure).
The beacon arrives but there is no Ship to selector.The store has only one market.Expected. Ask the merchant to add a second market if they want the selector.
The old currency picker is still visible."Neutralize legacy currency code left in the theme" is off, or the old picker uses markup the kit doesn't know.Switch the setting on. If it is on, add the element to the cleanup list.
Old and new code both load during the transition.The old script tag still exists.Expected until Stage 4. The embed loads itself only once, hides old pickers and keeps the price Shopify rendered. Finish Stage 4.
A product shows a huge price in a market, for example 299,006 MXN.The old per-country variant stores a local price, and Markets converts it again.Run Stage 5, then the merchant removes the legacy variant.
A product shows Sold out.Its variants have no available stock (common on test stores, and on legacy country variants whose stock was split).A store setting, outside the kit. The merchant sets the available quantity per variant (Products → the product → Inventory).
"Prices by country can't be read yet"The store hasn't granted read_products, write_products or read_markets.The merchant approves the app's updated scopes, then Re-scan store.
Queue: 423 locked (usually temporary)Shopify locked the store for a while.The app waits 15 minutes, doubling up to 6 hours. Click Run queue now later. Don't count the store as lost.
Queue: 402 frozen storeThe store is frozen (unpaid balance). Its tags can't be deleted.Unreachable list, with the cause, for a Shopify support ticket.
Queue: 404 store closed or app uninstalledThe store is closed or uninstalled the app.Unreachable list.
Queue: 401/403 token rejectedThe token is invalid, or it is a non-expiring token after January 1, 2027.The merchant opens the app again to re-authenticate. If it repeats, escalate.
Queue: missing scope: …write_script_tags…The store re-authorized without write_script_tags.The merchant approves the scope again. Never drop this scope before the audit reads zero.
Embed state Unreadable.The app couldn't read config/settings_data.json.Escalate (section 7).
app.extensions() shows "not read yet (needs the merchant in the app)".Nobody has opened the app in the admin since the change.Open the app in the store admin. It doesn't block verification.

6. What the kit can't do

Say these limits early, to the merchant and to your team:

The app shows the same list in the column What this kit can't do.

The column What this kit can't do, as the app shows it
The column What this kit can't do, as the app shows it

7. When to escalate to the lead developer

Handle everything else with this runbook. Call the lead developer only when:

  1. The live proof says "Shopify created the script tag".
  2. The embed is Enabled, you visited the storefront, the beacon setting is on, and the <html> tag in the storefront has no data-emk="running" (developer tools → Elements).
  3. The embed state is Unreadable.
  4. The token card is red: "Non-expiring offline token".
  5. A queue job is failed, or a 401/403 token rejected comes back after the merchant re-authenticated.
  6. "Migration for … stopped" shows an error you can't match with section 5.
  7. The theme cleanup needs more than removing the lines in the checklist (custom code around them).

When you escalate, attach the store's JSON report and a screenshot of the section that shows the problem.

8. Per-store checklist

Print one per store.

9. The report and the migration board

Each store gives one report. Merge the CSV files of all stores into one sheet: that is the migration board.

The CSV has one row per finding, with these columns:

ColumnContent
shopThe store.
sectionscript_tag, embed, legacy_marker or verdict.
themeThe theme name (empty for script tags).
roleMAIN for the live theme, or the theme's role.
itemThe script tag ID, the embed handle, or file:line of a marker.
detailThe script source, the activation link, the marker label, or the verdict reasons.
statusregistered, the embed state, the marker type, or the store status (Still registered, Active, Removed).

Useful views on the board:

The JSON file has the full report: script tags, each theme with its embed state, markers and last beacon, the verdict, the token and the queue.