links.arjun.tv/docs/end-user/chrome-extension-example-walkthrough

Chrome Extension — Full Walkthrough

This walkthrough takes you through a complete scenario from start to finish. You will:

  1. Map an e-commerce domain to a 10x.in handle.
  2. Create two experiments: an exit-intent popup and a scroll-triggered banner.
  3. Install the Chrome extension and see both experiments fire.

Scenario

Acme Store (acmestore.com) wants to:

  • Show a 10% discount popup when visitors try to leave the page.
  • Show a free shipping banner after visitors scroll past 50% of the page.

The marketer has a 10x.in handle acmestore and will map the subdomain go.acmestore.com.


Part 1: Backend setup (Marketer)

Set up your environment:

export OWNER_JWT="eyJ_owner_jwt_here"
export TOKEN="patv1_your_token_here" # or a CREATOR JWT for chain-rule writes
export API="https://api.10x.in"
export HANDLE="acmestore"

1.1 Register the domain

curl -X POST "$API/v2/account/domains" \
  -H "Authorization: Bearer $OWNER_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "go.acmestore.com",
    "sourceType": "BYOD_HOSTNAME",
    "defaultHandle": "acmestore"
  }'

Expected response:

{
  "domain": {
    "domain": "go.acmestore.com",
    "status": "PENDING_DELEGATION",
    "statusReason": "awaiting_nameserver_delegation"
  },
  "verification": {
    "txtName": "_ls-verify.go.acmestore.com",
    "txtValue": "lsv1...."
  }
}

1.2 Configure DNS

Capture verification.txtName and verification.txtValue, then run reconcile once so 10x returns the delegated nameservers for your domain:

curl -X POST "$API/v2/account/domains/go.acmestore.com/reconcile" \
  -H "Authorization: Bearer $OWNER_JWT" \
  -H "Content-Type: application/json" \
  -d '{}'

Then fetch the domain record and use the returned values in DNS:

curl "$API/v2/account/domains/go.acmestore.com" \
  -H "Authorization: Bearer $OWNER_JWT"

Apply the current domain-management flow:

Record typeWhereValue source
NSRegistrar for go.acmestore.com or apex delegation pointdomain.nameservers[] from the API
TXTDelegated zoneverification.txtName + verification.txtValue from create response
Traffic recordDelegated zoneExact edge target shown in Domain Management / API details

Warning

Do not hardcode domains.10x.in for this flow. The current custom-domain contract uses the nameservers and edge target returned for your domain after reconcile.

1.3 Verify and reconcile

After DNS propagation, trigger reconciliation again:

curl -X POST "$API/v2/account/domains/go.acmestore.com/reconcile" \
  -H "Authorization: Bearer $OWNER_JWT" \
  -H "Content-Type: application/json" \
  -d '{}'

Check the status:

curl "$API/v2/account/domains/go.acmestore.com" \
  -H "Authorization: Bearer $OWNER_JWT"

Look for "status": "ACTIVE" in the response. If it still says PENDING_DELEGATION, PENDING_CERT, or PENDING_EDGE_DEPLOY, follow statusReason, wait for propagation, and reconcile again.

1.4 Confirm the public lookup works

This is the endpoint the Chrome extension calls:

curl "$API/v2/public/domain-lookup?domain=go.acmestore.com"
{
  "handle": "acmestore",
  "pathRules": [],
  "status": "ACTIVE"
}

Part 2: Create experiments

2.1 Exit-intent popup: "10% off if you stay"

This rule fires when the visitor moves their mouse above the browser viewport (the universal "about to close the tab" gesture).

curl -X PUT "$API/v2/handles/$HANDLE/chain-rules/exit-discount" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ruleId": "exit-discount",
    "enabled": true,
    "priority": 10,
    "triggerEvent": "exit_intent",
    "when": {},
    "chainConditions": { "require": [], "exclude": [] },
    "action": {
      "type": "show_popup",
      "template": "<div id=\"tenx-exit-overlay\" style=\"position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:99999;font-family:-apple-system,BlinkMacSystemFont,sans-serif\"><div style=\"background:white;padding:48px;border-radius:16px;text-align:center;max-width:420px;box-shadow:0 24px 48px rgba(0,0,0,0.2)\"><div style=\"font-size:48px;margin-bottom:16px\">🎁</div><h2 style=\"margin:0 0 8px;font-size:24px;color:#111\">Wait — take 10% off!</h2><p style=\"color:#666;margin:0 0 24px;font-size:15px;line-height:1.5\">Use code <strong>EXIT10</strong> at checkout.<br/>This offer disappears when you leave.</p><a href=\"/checkout?coupon=EXIT10\" style=\"background:#0B6B3A;color:white;padding:14px 32px;border-radius:8px;text-decoration:none;display:inline-block;font-weight:600;font-size:15px\">Claim My Discount</a><p style=\"margin:16px 0 0;font-size:13px;color:#999;cursor:pointer\" onclick=\"document.getElementById('tenx-exit-overlay').remove()\">No thanks, I'll pay full price</p></div></div>",
      "vars": { "coupon": "EXIT10", "discount": "10%" }
    }
  }'

2.2 Scroll banner: "Free shipping over $50"

This rule fires when the visitor scrolls past 50% of the page, indicating engagement.

curl -X PUT "$API/v2/handles/$HANDLE/chain-rules/scroll-shipping" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ruleId": "scroll-shipping",
    "enabled": true,
    "priority": 20,
    "triggerEvent": "scroll_50",
    "when": {},
    "chainConditions": { "require": [], "exclude": [] },
    "action": {
      "type": "show_banner",
      "template": "<div id=\"tenx-scroll-banner\" style=\"position:fixed;bottom:0;left:0;width:100%;background:#0B6B3A;color:white;padding:14px 20px;text-align:center;font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:14px;z-index:99998;display:flex;align-items:center;justify-content:center;gap:12px;box-shadow:0 -2px 12px rgba(0,0,0,0.15)\"><span>🚚 Free shipping on orders over $50!</span><a href=\"/shop\" style=\"background:white;color:#0B6B3A;padding:6px 16px;border-radius:6px;text-decoration:none;font-weight:600;font-size:13px\">Shop Now</a><span style=\"cursor:pointer;margin-left:8px;opacity:0.7\" onclick=\"document.getElementById('tenx-scroll-banner').remove()\">✕</span></div>"
    }
  }'

2.3 Verify experiments are active

Prefetch all decisions to confirm both rules are matched:

curl -X POST "$API/v2/public/chain/prefetch" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "acmestore",
    "triggerEvents": ["page_load", "exit_intent", "scroll_50", "scroll_75", "idle_30s"]
  }'

Expected response (abbreviated):

{
  "decisions": {
    "page_load": { "matched": false },
    "exit_intent": {
      "matched": true,
      "ruleId": "exit-discount",
      "action": { "type": "show_popup", "template": "..." }
    },
    "scroll_50": {
      "matched": true,
      "ruleId": "scroll-shipping",
      "action": { "type": "show_banner", "template": "..." }
    },
    "scroll_75": { "matched": false },
    "idle_30s": { "matched": false }
  }
}

Both exit_intent and scroll_50 show "matched": true.


Part 3: Install and test the extension

3.1 Build and load the extension

cd chrome-extension
npm install
npm run build

Open chrome://extensions, enable Developer Mode, click Load unpacked, select chrome-extension/dist/.

3.2 Log in

Click the 10x.in icon in the toolbar. Either:

  • Email Login with your 10x.in credentials, or
  • PAT Token — paste your patv1_... token

3.3 Visit your domain

Navigate to https://go.acmestore.com (or any page on that domain).

What you should see:

  • The extension badge turns green with the number 2 (two active experiments).
  • Click the extension icon — the popup shows acmestore handle and "2 experiments running."

3.4 Trigger the scroll banner

Scroll down the page past the 50% mark.

Result: A green banner slides in at the bottom of the page: "Free shipping on orders over $50! Shop Now"

3.5 Trigger the exit-intent popup

Move your mouse cursor to the top of the browser window (above the page content, toward the tab bar).

Result: A centered popup overlay appears: "Wait — take 10% off!" with a "Claim My Discount" button.


Part 4: Extending the setup

Add a personalization rule for mobile visitors

Show a different CTA for mobile visitors:

curl -X PUT "$API/v2/handles/$HANDLE/personalization-rules/mobile-cta" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ruleId": "mobile-cta",
    "enabled": true,
    "priority": 10,
    "when": {
      "deviceIn": ["MOBILE"]
    },
    "variant": {
      "id": "mobile-variant",
      "cta": "Tap to Shop — Mobile Exclusive",
      "title": "Acme Store — Mobile Deals"
    }
  }'

Add a signal-based chain rule

Show a popup only if the visitor has viewed 3+ product pages in this session. This requires signals to be written first (the tracking SDK does this automatically when injected by the extension):

curl -X PUT "$API/v2/handles/$HANDLE/chain-rules/engaged-visitor-offer" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ruleId": "engaged-visitor-offer",
    "enabled": true,
    "priority": 5,
    "triggerEvent": "idle_30s",
    "when": {},
    "chainConditions": {
      "require": [
        { "signalKey": "page_view", "operator": "count_gte", "signalValue": "3" }
      ],
      "exclude": [],
      "minChainDepth": 3
    },
    "action": {
      "type": "show_popup",
      "template": "<div style=\"position:fixed;bottom:20px;right:20px;background:white;padding:24px;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,0.15);max-width:320px;z-index:99999;font-family:sans-serif\"><h3 style=\"margin:0 0 8px\">You seem interested!</h3><p style=\"color:#666;margin:0 0 16px;font-size:14px\">Chat with us about your needs.</p><a href=\"/contact\" style=\"background:#0B6B3A;color:white;padding:10px 20px;border-radius:6px;text-decoration:none;font-size:14px\">Start a Chat</a></div>"
    }
  }'

This rule only fires after the visitor has accumulated 3+ page_view signals in their session and then goes idle for 30 seconds.


Verification checklist

  • [ ] go.acmestore.com returns ACTIVE from domain-lookup
  • [ ] Chain prefetch returns 2 matched rules (exit-discount, scroll-shipping)
  • [ ] Extension badge shows green with "2" on go.acmestore.com
  • [ ] Scrolling past 50% triggers the shipping banner
  • [ ] Moving mouse to top of viewport triggers the discount popup
  • [ ] Popup "No thanks" link dismisses the overlay
  • [ ] Banner "✕" button dismisses the banner

Related guides