- Overview
- Application Architecture
- Component Structure
- Installation and Configuration
- Core Features
- Event System
- Catalog Synchronization
- Data Flows
- APIs and Integrations
- Detailed Code Structure
- Service Workers and Push Notifications
- Troubleshooting and Maintenance
The VTEX CleverTap App is a VTEX IO application that natively integrates the VTEX e-commerce platform with the CleverTap customer engagement platform. This integration enables automatic tracking of user behavior events, product catalog synchronization, and personalized campaign delivery through multiple channels.
- Automatic Tracking: Automatic capture of browsing, cart, checkout, and purchase events
- Catalog Synchronization: Automatic VTEX product synchronization to CleverTap catalog (every 24 hours)
- Personalized Campaigns: Support for Web Push, Web Pop-ups, Web Exit Intent, and Web Native Display
- Native Integration: No additional development or manual tags required
- VTEX IO Compliance: Fully compatible with VTEX IO architecture
- Current Version: 0.0.20
- Vendor: clevertappartnerbr
- Platform: VTEX IO
- Builders Used:
- Node.js 6.x (Backend)
- React 3.x (Frontend/Pixel)
- Store 0.x
- Pixel 0.x
- Service Worker 0.x
- Docs 0.x
The app supports all CleverTap regions:
us1- United Stateseu1- Europein1- Indiasg1- Singaporeaps3- Asia Pacificmec1- Middle East
┌─────────────────────────────────────────────────────────────────┐
│ VTEX STOREFRONT │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Pixel │───▶│ React App │───▶│ CleverTap │ │
│ │ (head.html)│ │ (Frontend) │ │ Web SDK │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ └────────────────────┴────────────────────┘ │
│ │ │
└──────────────────────────────┼──────────────────────────────────┘
│
▼
┌─────────────────────┐
│ localStorage │
│ (clevertapConfigs) │
└─────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ VTEX CHECKOUT │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ checkout6-custom.js (CDN Script) │ │
│ │ ├─ Checkout Started │ │
│ │ ├─ Payment Info │ │
│ │ ├─ Checkout Steps │ │
│ │ └─ Coupon Applied/Denied │ │
│ └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ VTEX BACKEND (Node.js) │
│ │
│ ┌─────────────────┐ ┌────────────────┐ │
│ │ OMS Events │───▶│ Middleware │ │
│ │ Broadcast │ │ Handlers │ │
│ └─────────────────┘ └────────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────────┐ │
│ │ │ Catalog Service │ │
│ │ └─────────────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────────┐ │
│ └──────────▶│ CleverTap Client │ │
│ └─────────────────────┘ │
└──────────────────────────────┼──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ CLEVERTAP PLATFORM │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Events │ │ Profiles │ │ Catalog │ │
│ │ API │ │ API │ │ API │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Dashboard & Campaign Management │ │
│ │ ├─ Segmentation │ │
│ │ ├─ Web Push │ │
│ │ ├─ Web Pop-ups │ │
│ │ └─ Exit Intent │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
- Pixel App (React): Initializes CleverTap SDK and listens to VTEX events
- Service Worker: Manages push notifications and offline functionality
- CleverTap Web SDK: JavaScript library that communicates with CleverTap
- Event Handlers: Process OMS (Order Management System) events
- Catalog Service: Manages product synchronization
- Middleware Layer: Validates and transforms data before sending
- CleverTap API Clients: HTTP clients for CleverTap communication
- VTEX API Clients: Clients to access VTEX APIs (Catalog, OMS, Master Data)
vtex-clevertap-app-main/
│
├── docs/ # Documentation
│ ├── README.md # Installation guide
│ ├── events-summary.md # Event summary
│ └── images/ # Screenshots
│
├── node/ # Backend (Node.js)
│ ├── clients/ # HTTP Clients
│ │ ├── clevertapCatalog.ts # CleverTap Catalog API Client
│ │ ├── vtexCatalog.ts # VTEX Catalog API Client
│ │ ├── md.ts # Master Data Client
│ │ └── index.ts # Client exports
│ │
│ ├── handlers/ # Event Handlers
│ │ └── catalogSyncHandler.ts # Sync handler
│ │
│ ├── middlewares/ # Middlewares
│ │ ├── catalog/
│ │ │ └── validateCatalogSync.ts
│ │ ├── error/
│ │ │ └── errorMiddleware.ts
│ │ └── oms/
│ │ └── omsFilteredEvents.ts # OMS event processor
│ │
│ ├── services/ # Business Services
│ │ └── CatalogService.ts # Synchronization service
│ │
│ ├── utils/ # Utilities
│ │ ├── get-categories.ts
│ │ ├── get-payment-method.ts
│ │ ├── get-total.ts
│ │ └── normalize-items.ts
│ │
│ ├── lib/ # Helper Libraries
│ │ └── clevertap/
│ │ ├── getConfig.ts # Get configurations
│ │ └── index.ts
│ │
│ ├── routes/ # HTTP Routes
│ │ └── catalog.ts
│ │
│ ├── index.ts # Backend entry point
│ ├── service.json # Service configuration
│ └── package.json
│
├── react/ # Frontend (React/Pixel)
│ ├── modules/ # Event Modules
│ │ ├── enhancedEcommerceEvents.ts # E-commerce events
│ │ ├── legacyEvents.ts # Legacy events
│ │ ├── manageEvents.ts # Event management
│ │ ├── requestEvents.ts # Request events
│ │ └── utils/
│ │ ├── format-cart-summary.ts
│ │ ├── get-categories.ts
│ │ ├── get-payment-method.ts
│ │ ├── get-price.ts
│ │ ├── get-quantity.ts
│ │ ├── get-seller.ts
│ │ ├── storage-item.ts
│ │ └── index.ts
│ │
│ ├── lib/ # Libraries
│ │ └── clevertap.ts # CleverTap initialization
│ │
│ ├── typings/ # TypeScript Definitions
│ │ ├── clevertap.d.ts
│ │ ├── events.d.ts
│ │ └── global.d.ts
│ │
│ ├── index.tsx # Pixel entry point
│ └── package.json
│
├── pixel/ # Pixel (injected in head)
│ └── head.html # Initialization script
│
├── service-workers/ # Service Workers
│ ├── header.js # Imports CleverTap SW
│ ├── activate.js
│ ├── install.js
│ ├── push.js
│ ├── fetch.js
│ ├── message.js
│ └── sync.js
│
├── store/ # Store configurations
│ ├── interfaces.json
│ └── plugins.json
│
├── public/ # Public assets
│ └── metadata/
│ ├── messages/ # Translations
│ ├── licenses/ # Licenses
│ └── images/
│
├── manifest.json # VTEX app manifest
├── package.json
└── yarn.lock
Before installing the app, ensure you have:
- VTEX IO Account: Active VTEX store with IO support
- CleverTap Account: Active CleverTap account with:
- Project ID (Account ID)
- Account Passcode
- Region information
- Admin Access: VTEX admin permissions to install apps
-
Access your VTEX Admin:
https://{your-account}.myvtex.com/admin -
Navigate to Apps → App Store
-
Search for "VTEX CleverTap Pixel App"
-
Click Install
-
Confirm the installation
After installation, configure the app with your CleverTap credentials:
-
Go to Apps → My Apps → VTEX CleverTap Pixel App
-
Click on Settings
-
Fill in the required fields:
| Field | Description | Example | Required |
|---|---|---|---|
| CleverTap Project ID | Your CleverTap Account ID | XXX-XXX-XXX |
Yes |
| CleverTap Account Passcode | Authentication passcode | XXX-XXX-XXXX |
Yes |
| CleverTap Region | Your account region | us1, eu1, in1, sg1 |
Yes |
- Configure Preference Settings:
- Default:
true - Description: Enables event tracking for non-authenticated users
- Use Case: Track anonymous browsing behavior before login
Enable/disable specific events to track:
Storefront Events:
vtex:search- Products Searchedvtex:filterManipulation- Product Filtered By CategorycategoryView- Product List Viewedvtex:promoView- Promotion Viewedvtex:promotionClick- Promotion Clickedvtex:productView- Product Viewedvtex:productClick- Product Clickedvtex:addToCart- Product Added To Cartvtex:removeFromCart- Product Removed From Cartvtex:viewCart- Cart Viewedvtex:addToWishlist- Product Added to Wishlistvtex:removeToWishlist- Product Removed from Wishlistvtex:share- Product Sharedvtex:orderPlaced- Order Created
Checkout Events:
add_to_cart- Checkout Product Added To Cartremove_from_cart- Checkout Product Removed From Cartview_cart- Checkout Cart Viewedbegin_checkout- Checkout Startedadd_payment_info- Payment Infocheckout_step_viewed- Checkout Step Viewed/Completedcoupon_applied- Coupon Appliedcoupon_denied- Coupon Denied
- Default:
true - Description: Automatically syncs product catalog every 24 hours
- Note: When enabled, Integration Email becomes mandatory
- Required if: Catalog Sync is enabled
- Description: Email address to receive sync notifications
- Format: Valid email address
- Click Save
The checkout operates independently from the storefront. To track checkout events, you must add a script to the checkout configuration.
-
Navigate to:
Store Settings → Checkout → Code → checkout6-custom.js -
Insert the following script:
;(function () {
// CleverTap Checkout Events Script
var scriptUrl =
'https://cdn.jsdelivr.net/gh/GuiGiesbrecht/vtex-clevertap-scripts/clevertap-checkout-events.js'
var script = document.createElement('script')
script.src = scriptUrl
script.type = 'text/javascript'
script.async = true
document.head.appendChild(script)
})()- Save or publish the checkout configuration
In some VTEX Admin versions:
-
Navigate to:
Store Settings → Checkout UI Custom → JavaScript -
Insert the same script as above
-
Save changes
- Checkout Started
- Payment Info
- Checkout Steps
- Coupon Applied/Denied
- Cart Abandonment data will be incomplete
After configuration, verify the integration:
-
Open your storefront in a browser
-
Perform test actions:
- Search for products
- View product details
- Add items to cart
- Start checkout
- Complete a test order
-
Check CleverTap Dashboard:
- Go to Events section
- Verify events are appearing:
Product ViewedProduct Added to CartCheckout StartedOrder Created
-
Check Browser Console:
- No CleverTap errors should appear
- You should see CleverTap initialization messages
The app automatically tracks user interactions across the entire shopping journey.
These events are captured by the React pixel app:
Search & Discovery
Products Searched: Triggered when user performs a searchProduct Filtered: When filters are applied to product listingsProduct List Viewed: When a category or collection page is viewed
Product Engagement
Product Viewed: When a product detail page is loadedProduct Clicked: When a product is clicked from a listPromotion Viewed: When promotional banners are viewedPromotion Clicked: When promotional content is clicked
Shopping Cart
Product Added to Cart: Item added to shopping cartProduct Removed from Cart: Item removed from cartCart Viewed: Shopping cart page viewed
Wishlist
Product Added to Wishlist: Item added to wishlistProduct Removed from Wishlist: Item removed from wishlist
Social
Product Shared: Product shared via social channels
These events are captured by the Node.js backend:
Order States
Charged: Order payment approved (payment-approvedstate)Order Cancelled: Order cancelled by user or system (canceledstate)Checkout Failed: Payment denied or incomplete order (payment-denied,incompletestates)
The app automatically syncs customer data to CleverTap profiles:
Profile Properties:
{
Name: "Customer Full Name",
Email: "customer@email.com",
Phone: "+1234567890",
MSG-email: false,
MSG-push: true,
MSG-sms: true,
MSG-whatsapp: true
}Data Sources:
- VTEX Session API (
/api/sessions) - Master Data (CL entity)
- Order Profile Data
Sync Trigger:
- On user login (
vtex:userDataevent) - On order placement (via Master Data lookup)
Automatic product catalog sync with CleverTap's Catalog API.
-
Trigger: Runs every 24 hours after first order with
payment-approvedstatus -
Process:
- Fetches all SKU IDs from VTEX Catalog
- Retrieves detailed SKU information
- Transforms to CleverTap catalog format
- Converts to CSV
- Uploads via presigned S3 URL
- Notifies CleverTap to process
-
Data Mapped:
{
Identity: "SKU-123", // SKU ID
ProductId: "PROD-456", // Product ID
SkuId: "SKU-123", // SKU ID
Ean: "7891234567890", // EAN barcode
RefId: "REF-789", // Reference ID
Name: "Product Name - Variant", // Full name
ImageUrl: "https://...", // Image URL
Categories: "Cat1 - Cat2", // Category path
BrandName: "Brand", // Brand name
SalesChannels: "1,2,3", // Sales channels
CommercialCondition: 1 // Commercial condition
}- Storage: Last sync timestamp stored in VBase (
config/lastCatalogSync)
While not exposed by default, catalog sync can be triggered manually by:
- Enabling the route in
service.json - Making a POST request to the catalog endpoint
┌─────────────────────────────────────────────────────────────┐
│ USER INTERACTION │
│ (Browse, Click, Add to Cart, etc.) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ VTEX EVENT SYSTEM │
│ (vtex.pixel-interfaces) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CLEVERTAP PIXEL APP │
│ (window.postMessage listener) │
│ │
│ 1. Receive VTEX event │
│ 2. Check if event is enabled │
│ 3. Check user authentication status │
│ 4. Transform data to CleverTap format │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CLEVERTAP WEB SDK │
│ (clevertap.event.push) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CLEVERTAP PLATFORM │
│ (Event Processing & Storage) │
└─────────────────────────────────────────────────────────────┘
VTEX Event Input:
{
eventName: 'vtex:productView',
product: {
productId: '123',
productName: 'Blue T-Shirt',
brand: 'BrandName',
categories: ['Apparel', 'T-Shirts'],
items: [{
skuId: 'SKU-456',
variant: 'Blue/M',
price: 29.99,
quantity: 1,
imageUrl: 'https://...',
sellers: [{
sellerId: '1',
sellerName: 'Main Seller'
}]
}]
}
}CleverTap Event Output:
{
event_name: 'Product Viewed',
event_data: {
product_id: '123',
sku_id: 'SKU-456',
name: 'Blue T-Shirt',
brand: 'BrandName',
category: 'Apparel > T-Shirts',
variant: 'Blue/M',
price: 29.99,
quantity: 1,
url: 'https://store.com/blue-t-shirt/p',
image_url: 'https://...',
seller_id: '1',
seller_name: 'Main Seller'
}
}Before sending an event to CleverTap, the app performs these checks:
// 1. Check if event is enabled in settings
if (!verifyEvent(eventName)) {
return // Event disabled
}
// 2. Check user authentication
const isLogged = verifyIsLogged()
const allowUnknown = verifyIsUnknownEvents()
// 3. Decide whether to send
if (!allowUnknown && !isLogged) {
return // User not logged in and unknown events disabled
}
// 4. Add delay to ensure user data is loaded
await new Promise(resolve => setTimeout(resolve, 500))
// 5. Send event to CleverTap
clevertap.event.push(transformedEvent)The app subscribes to specific order state changes:
{
"sender": "vtex.orders-broadcast",
"topics": ["canceled", "payment-approved", "incomplete", "payment-denied"]
}┌─────────────────────────────────────────────────────────────┐
│ ORDER STATE CHANGE IN VTEX │
│ (canceled, payment-approved, etc.) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ OMS EVENT BROADCAST │
│ (vtex.orders-broadcast) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ CLEVERTAP APP BACKEND │
│ (omsFilteredEvents middleware) │
│ │
│ 1. Check if order already processed │
│ 2. Fetch full order data from OMS │
│ 3. Fetch customer email from Master Data │
│ 4. Map state to CleverTap event │
│ 5. Send event to CleverTap API │
│ 6. Check catalog sync schedule │
└──────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CLEVERTAP UPLOAD API │
│ (Profile & Event Ingestion) │
└─────────────────────────────────────────────────────────────┘
The app prevents duplicate events using an in-memory cache:
const processedOrders = new Map<string, Set<string>>()
// Structure:
// {
// "order-123": Set(['payment-approved', 'canceled']),
// "order-456": Set(['payment-approved'])
// }Logic:
- Check if order ID exists in cache
- Check if current state was already processed for this order
- If not processed, add to cache and proceed
- If already processed, skip
┌─────────────────────────────────────────────────────────────┐
│ TRIGGER POINT │
│ (24h after last sync + order approved) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CATALOG SERVICE INITIALIZATION │
│ (CatalogService.syncCatalog()) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ STEP 1: FETCH ALL SKU IDS │
│ (vtexCatalog.getSKUIds - paginated) │
│ │
│ GET /api/catalog_system/pvt/sku/stockkeepingunitids │
│ Pagination: 20 SKUs per page │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ STEP 2: FETCH DETAILED SKU DATA │
│ (vtexCatalog.getSKUAndContext - sequential) │
│ │
│ For each SKU ID: │
│ GET /api/catalog_system/pvt/sku/stockkeepingunitbyid/:id │
│ • Filter out inactive SKUs │
│ • Transform to CleverTap format │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ STEP 3: CONVERT TO CSV │
│ (json2csv library) │
│ │
│ CSV Columns: │
│ Identity,ProductId,SkuId,Ean,RefId,Name,ImageUrl, │
│ Categories,BrandName,SalesChannels,CommercialCondition │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ STEP 4: REQUEST PRESIGNED S3 URL │
│ (clevertapCatalog.postCatalogUrl()) │
│ │
│ POST https://{region}.api.clevertap.com/get_catalog_url │
│ Headers: │
│ • X-CleverTap-Account-Id │
│ • X-CleverTap-Passcode │
│ │
│ Response: { presignedS3URL: "https://..." } │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ STEP 5: UPLOAD CSV TO S3 │
│ (clevertapCatalog.uploadCatalog()) │
│ │
│ PUT {presignedS3URL} │
│ Body: CSV Buffer │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ STEP 6: NOTIFY CLEVERTAP │
│ (clevertapCatalog.completeUpload()) │
│ │
│ POST https://{region}.api.clevertap.com/upload_catalog_completed │
│ Body: │
│ { │
│ name: "catalog_{accountName}", │
│ email: "{integrationEmail}", │
│ creator: "{integrationEmail}", │
│ url: "{presignedS3URL}", │
│ replace: true/false │
│ } │
└──────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ STEP 7: UPDATE SYNC TIMESTAMP │
│ (vbase.saveJSON('config', 'lastCatalogSync')) │
│ │
│ Stores: ISO timestamp of successful sync │
└─────────────────────────────────────────────────────────────┘
VTEX SKU Data:
{
Id: 123,
ProductId: 456,
NameComplete: "Blue T-Shirt - Size M",
ProductName: "Blue T-Shirt",
SkuName: "Size M",
IsActive: true,
ImageUrl: "https://store.vteximg.com/...",
AlternateIds: {
Ean: "7891234567890",
RefId: "REF-123"
},
ProductCategories: {
"1": "Apparel",
"2": "T-Shirts"
},
BrandName: "BrandName",
SalesChannels: [1, 2, 3],
CommercialConditionId: 1
}CleverTap Catalog Format:
{
Identity: "123",
ProductId: "456",
SkuId: "123",
Ean: "7891234567890",
RefId: "REF-123",
Name: "Blue T-Shirt - Size M",
ImageUrl: "https://store.vteximg.com/...",
Categories: "Apparel - T-Shirts",
BrandName: "BrandName",
SalesChannels: "1,2,3",
CommercialCondition: "1"
}// Check last sync time
const lastSync = await vbase.getJSON('config', 'lastCatalogSync')
const lastRun = new Date(lastSync).getTime()
const hoursSince = (Date.now() - lastRun) / 1000 / 60 / 60
// Only sync if 24+ hours passed
if (hoursSince >= 24) {
await catalogSyncHandler(ctx)
await vbase.saveJSON('config', 'lastCatalogSync', new Date().toISOString())
}try {
await catalogSyncHandler(ctx, { replace: true })
} catch (error) {
if (error.message.includes('No Catalog with given name')) {
// Retry without replace flag to create new catalog
await catalogSyncHandler(ctx, { replace: false })
}
}for (const skuId of skuIds) {
try {
const skuData = await vtexCatalog.getSKUAndContext(skuId)
// Process SKU
} catch (err) {
console.error(`Error processing SKU ${skuId}: ${err}`)
// Continue with next SKU (fail gracefully)
}
}┌───────────────────────────────────────────────────────────┐
│ USER LOGS INTO VTEX STORE │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ VTEX SESSION API UPDATED │
│ (profile data stored in session) │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ vtex:userData EVENT FIRED │
│ (sent via window.postMessage) │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ CLEVERTAP PIXEL APP RECEIVES EVENT │
│ (enhancedEcommerceEvents.ts) │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ FETCH PROFILE FROM SESSION API │
│ GET /api/sessions?items=profile.firstName,lastName, │
│ email,phone │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ BUILD CLEVERTAP PROFILE OBJECT │
│ { │
│ Site: { │
│ Name: "Full Name", │
│ Email: "email@domain.com", │
│ Phone: "+1234567890", │
│ MSG-email: false, │
│ MSG-push: true, │
│ MSG-sms: true, │
│ MSG-whatsapp: true │
│ } │
│ } │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ SEND TO CLEVERTAP │
│ clevertap.onUserLogin.push(profileData) │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ UPDATE LOCAL CONFIG │
│ localStorage.setItem('clevertapConfigs', { │
│ ...config, │
│ isLogged: true │
│ }) │
└───────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────┐
│ USER CLICKS "ADD TO CART" BUTTON │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ VTEX MINICART COMPONENT UPDATED │
│ (product added to orderForm) │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ vtex:addToCart EVENT FIRED │
│ { │
│ items: [{ │
│ skuId: "SKU-123", │
│ quantity: 1, │
│ price: 29.99, │
│ name: "Product Name", │
│ ... │
│ }] │
│ } │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ CLEVERTAP PIXEL RECEIVES EVENT │
│ (enhancedEcommerceEvents.ts) │
└──────────────────────────┬────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ VALIDATION CHECKS │
│ 1. Is event enabled? │
│ 2. Is user logged in OR unknown events allowed? │
└──────────────────────────┬────────────────────────────────┘
│
▼ (if valid)
┌────────────────────────────────────────────────────────────┐
│ TRANSFORM DATA TO CLEVERTAP FORMAT │
│ { │
│ product_id: "PROD-456", │
│ sku_id: "SKU-123", │
│ name: "Product Name", │
│ price: 29.99, │
│ quantity: 1, │
│ category: "Cat1 > Cat2", │
│ brand: "Brand Name", │
│ variant: "Blue/M", │
│ seller_id: "1", │
│ ... │
│ } │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ SEND TO CLEVERTAP │
│ clevertap.event.push( │
│ 'Product Added to Cart', │
│ transformedData │
│ ) │
└───────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ USER COMPLETES PAYMENT │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ VTEX OMS CREATES ORDER │
│ (status: 'payment-pending') │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ PAYMENT GATEWAY PROCESSES PAYMENT │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ ORDER STATUS CHANGES TO 'payment-approved' │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ vtex.orders-broadcast FIRES EVENT │
│ { │
│ domain: "Fulfillment", │
│ orderId: "ORDER-123", │
│ currentState: "payment-approved", │
│ lastState: "payment-pending" │
│ } │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ CLEVERTAP BACKEND RECEIVES EVENT │
│ (omsFilteredEvents middleware) │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ CHECK DEDUPLICATION CACHE │
│ if (processedOrders[orderId].has('payment-approved')│
│ return │
└──────────────────────────┬─────────────────────────────────┘
│
▼ (if not processed)
┌────────────────────────────────────────────────────────────┐
│ FETCH FULL ORDER DATA │
│ GET /api/oms/pvt/orders/{orderId} │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ FETCH CUSTOMER EMAIL │
│ MD.searchDocuments({ │
│ dataEntity: 'CL', │
│ where: `userId=${userProfileId}` │
│ }) │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ BUILD EVENT PAYLOAD │
│ { │
│ order_id: "ORDER-123", │
│ checkout_id: "abc123", │
│ state: "payment-approved", │
│ affiliation: "Seller Name", │
│ value: 129.99, │
│ revenue: 99.99, │
│ shipping: 10.00, │
│ tax: 20.00, │
│ discount: 0, │
│ payment_method: "Credit Card", │
│ currency: "USD", │
│ coupon: "" │
│ } │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ SEND TO CLEVERTAP UPLOAD API │
│ clevertap.upload([{ │
│ identity: "customer@email.com", │
│ type: "event", │
│ objectId: "back-end-event", │
│ evtName: "Charged", │
│ evtData: payload │
│ }]) │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ ADD TO DEDUPLICATION CACHE │
│ processedOrders[orderId].add('payment-approved') │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ CHECK CATALOG SYNC SCHEDULE │
│ if (24h+ since last sync && catalogSync enabled) │
│ trigger catalogSyncHandler() │
└────────────────────────────────────────────────────────────┘
The app integrates with multiple CleverTap APIs:
Endpoint: https://{region}.api.clevertap.com/1/upload
Authentication:
X-CleverTap-Account-Id: {accountID}
X-CleverTap-Passcode: {accountPasscode}Request Body:
{
"d": [
{
"identity": "user@email.com",
"type": "event",
"evtName": "Product Viewed",
"evtData": {
"product_id": "123",
"price": 29.99
}
}
]
}Profile Update:
{
"d": [
{
"identity": "user@email.com",
"type": "profile",
"profileData": {
"Name": "John Doe",
"Email": "user@email.com",
"Phone": "+1234567890"
}
}
]
}Get Presigned URL:
POST https://{region}.api.clevertap.com/get_catalog_url
Headers:
X-CleverTap-Account-Id: {accountID}
X-CleverTap-Passcode: {accountPasscode}
Response:
{
"presignedS3URL": "https://s3.amazonaws.com/..."
}Upload CSV to S3:
PUT {presignedS3URL}
Content-Type: text/csv
Body: CSV dataComplete Upload:
POST https://{region}.api.clevertap.com/upload_catalog_completed
Headers:
X-CleverTap-Account-Id: {accountID}
X-CleverTap-Passcode: {accountPasscode}
Body:
{
"name": "catalog_accountname",
"email": "integration@email.com",
"creator": "integration@email.com",
"url": "{presignedS3URL}",
"replace": true
}Get Order Details:
GET https://{accountName}.vtexcommercestable.com.br/api/oms/pvt/orders/{orderId}
Headers:
X-VTEX-API-AppToken: {appToken}
X-VTEX-API-AppKey: {appKey}Response Structure:
{
"orderId": "ORDER-123",
"orderFormId": "abc123",
"value": 12999,
"items": [...],
"clientProfileData": {
"userProfileId": "user-456",
"email": "customer@email.com"
},
"paymentData": {
"transactions": [{
"payments": [{
"paymentSystem": "Credit Card",
"value": 12999
}]
}]
},
"totals": [
{ "id": "Items", "value": 9999 },
{ "id": "Shipping", "value": 1000 },
{ "id": "Tax", "value": 2000 }
],
"sellers": [{
"name": "Main Seller"
}],
"marketingData": {
"coupon": "DISCOUNT10"
}
}Get SKU IDs (Paginated):
GET https://{accountName}.vtexcommercestable.com.br/api/catalog_system/pvt/sku/stockkeepingunitids?page={page}&pagesize={pageSize}
Response:
{
"range": {
"total": 1000,
"from": 1,
"to": 20
},
"items": [123, 456, 789, ...]
}Get SKU Details:
GET https://{accountName}.vtexcommercestable.com.br/api/catalog_system/pvt/sku/stockkeepingunitbyid/{skuId}
Response:
{
"Id": 123,
"ProductId": 456,
"IsActive": true,
"NameComplete": "Product Name - Variant",
"ProductName": "Product Name",
"SkuName": "Variant",
"ImageUrl": "https://...",
"AlternateIds": {
"Ean": "7891234567890",
"RefId": "REF-123"
},
"ProductCategories": {
"1": "Category 1",
"2": "Category 2"
},
"BrandName": "Brand Name",
"SalesChannels": [1, 2, 3],
"CommercialConditionId": 1
}Search Documents:
GET https://api.vtex.com/{accountName}/dataentities/CL/search?_fields=email&_where=userId={userId}
Headers:
X-VTEX-API-AppToken: {appToken}
X-VTEX-API-AppKey: {appKey}
Response:
[
{
"email": "customer@email.com"
}
]Get User Profile:
GET https://{accountName}.myvtex.com/api/sessions?items=profile.firstName,profile.lastName,profile.email,profile.phone
Response:
{
"namespaces": {
"profile": {
"firstName": { "value": "John" },
"lastName": { "value": "Doe" },
"email": { "value": "john@email.com" },
"phone": { "value": "+1234567890" }
}
}
}The app requires these policies (defined in manifest.json):
{
"policies": [
{
"name": "outbound-access",
"attrs": {
"host": "eu1.api.clevertap.com",
"path": "/*"
}
},
{
"name": "OMSViewer"
},
{
"name": "ADMIN_DS"
},
{
"name": "colossus-fire-event"
},
{
"name": "vbase-read-write"
}
]
}Policy Descriptions:
outbound-access: Allows HTTP requests to CleverTap APIsOMSViewer: Read access to Order Management SystemADMIN_DS: Access to Master Datacolossus-fire-event: Event broadcastingvbase-read-write: Storage for sync timestamps
import { canUseDOM } from 'vtex.render-runtime'
import { initCleverTap } from './lib/clevertap'
import { sendEnhancedEcommerceEvents } from './modules/enhancedEcommerceEvents'
import { sendLegacyEvents } from './modules/legacyEvents'
export function handleEvents(e: PixelMessage) {
sendEnhancedEcommerceEvents(e)
sendLegacyEvents(e)
}
if (canUseDOM) {
initCleverTap() // Initialize CleverTap SDK
window.addEventListener('message', handleEvents) // Listen to VTEX events
}Flow:
- Check if running in browser (
canUseDOM) - Initialize CleverTap SDK with stored configuration
- Add event listener for
window.postMessageevents - Route events to appropriate handlers
export function initCleverTap() {
// 1. Retrieve configuration from localStorage
const savedConfig = localStorage.getItem('clevertapConfigs')
const config = JSON.parse(savedConfig)
// 2. Validate configuration
if (!config?.accountID || !config?.region) {
console.error('CleverTap: no valid configuration found.')
return null
}
// 3. Initialize CleverTap SDK
clevertap.init(config.accountID, config.region)
clevertap.privacy.push(config.privacy)
clevertap.spa = config.spa
// 4. Configure push notifications
initClevertapNotifications()
return clevertap
}export async function sendEnhancedEcommerceEvents(e: PixelMessage) {
switch (e.data.eventName) {
case 'vtex:productView': {
// Wait for user data to load
await new Promise(resolve => setTimeout(resolve, 500))
// Check permissions
const isUnknownEvents = verifyIsUnknownEvents()
const isLogged = verifyIsLogged()
if (!isUnknownEvents && !isLogged) return
// Check if event is enabled
if (verifyEvent('vtex:productView')) {
productView(e.data)
}
break
}
// ... other cases
}
}Example: Product View Event
export function productView(data: any) {
const { product } = data
const item = product.items[0]
const eventData = {
product_id: product.productId,
sku_id: item.skuId,
name: product.productName,
brand: product.brand,
category: getCategories(product.categories),
variant: item.name,
price: getPrice(item),
quantity: getQuantity(item),
url: window.location.href,
image_url: item.imageUrl,
seller_id: getSeller(item)?.sellerId,
seller_name: getSeller(item)?.sellerName,
}
clevertap.event.push('Product Viewed', eventData)
}import { Service } from '@vtex/api'
import { Clients } from './clients'
import { omsFilteredEvents } from './middlewares/oms/omsFilteredEvents'
const clients: ClientsConfig<Clients> = {
implementation: Clients,
options: {
default: {
retries: 2,
timeout: 800,
},
},
}
export default new Service({
clients,
events: {
omsFilteredEvents, // Subscribe to OMS broadcasts
},
})const processedOrders = new Map<string, Set<string>>()
export async function omsFilteredEvents(
ctx: StatusChangeContext,
next: () => Promise<any>
) {
const { orderId, currentState } = ctx.body
// 1. Deduplication check
if (!processedOrders.has(orderId)) {
processedOrders.set(orderId, new Set())
}
const statesSet = processedOrders.get(orderId)
if (statesSet.has(currentState)) return next()
statesSet.add(currentState)
// 2. Get settings
const settings = await getConfig(ctx)
const clevertap = await getCleverTap(ctx)
// 3. Fetch order data
const response = await ctx.clients.oms.order(orderId, 'AUTH_TOKEN')
// 4. Fetch customer email from Master Data
const mdResponse = await ctx.clients.MD.searchDocuments({
dataEntity: 'CL',
fields: ['email'],
where: `userId=${response.clientProfileData.userProfileId}`,
})
// 5. Build event payload
const payload = {
order_id: orderId,
state: currentState,
value: response.value,
payment_method: getPaymentMethodsString(
response.paymentData.transactions[0].payments
),
// ... more fields
}
// 6. Map state to event name
const eventMap = {
canceled: { name: 'Order Cancelled' },
'payment-approved': { name: 'Charged' },
incomplete: { name: 'Checkout Failed', includeItems: true },
'payment-denied': { name: 'Checkout Failed', includeItems: true },
}
const event = eventMap[currentState]
// 7. Send to CleverTap
if (event) {
const data = {
identity: mdResponse[0].email,
type: 'event',
evtName: event.name,
evtData: payload,
}
clevertap.upload([data])
}
// 8. Check catalog sync
if (
settings.preferences.catalogSync &&
settings.preferences.integrationEmail
) {
await handleCatalogSync(ctx, settings.preferences.integrationEmail)
}
await next()
}export class CatalogService {
private vtexCatalogClient: VtexCatalog
private clevertapCatalogClient: ClevertapCatalog
private pageSize = 20
public async syncCatalog(ctx: Context, options: SyncCatalogOptions) {
try {
// 1. Get all SKU IDs
const skuIds = await this.getAllSkuIds()
console.info(`Processed: ${skuIds.length} skus`)
// 2. Build CleverTap SKU objects
const clevertapSkus = await this.buildClevertapSkus(skuIds)
// 3. Convert to CSV
const csv = json2csv(clevertapSkus)
// 4. Get presigned S3 URL
const {
presignedS3URL,
} = await this.clevertapCatalogClient.postCatalogUrl(ctx)
// 5. Upload CSV to S3
await this.clevertapCatalogClient.uploadCatalog(csv, presignedS3URL, ctx)
// 6. Notify CleverTap
await this.clevertapCatalogClient.completeUpload(presignedS3URL, ctx, {
name: `catalog_${options.accountName}`,
email: options.email,
creator: options.creator,
replace: options.replace,
})
console.info('Load completed')
} catch (err) {
console.error(`Error processing load: ${err}`)
throw err
}
}
private async getAllSkuIds(): Promise<number[]> {
const skuIds: number[] = []
let page = 1
let pageSkuIds: number[] = []
// Paginate through all SKUs
do {
pageSkuIds = await this.vtexCatalogClient.getSKUIds(page, this.pageSize)
if (!pageSkuIds?.length) break
skuIds.push(...pageSkuIds)
page++
} while (pageSkuIds.length === this.pageSize)
return skuIds
}
private async buildClevertapSkus(skuIds: number[]): Promise<any[]> {
const clevertapSkus: any[] = []
for (const skuId of skuIds) {
try {
const skuData = await this.vtexCatalogClient.getSKUAndContext(skuId)
// Skip inactive SKUs
if (!skuData.IsActive) continue
clevertapSkus.push(this.mapToClevertapSku(skuData))
} catch (err) {
console.error(`Error processing SKU ${skuId}: ${err}`)
}
}
return clevertapSkus
}
private mapToClevertapSku(skuData: any) {
return {
Identity: skuData.Id,
ProductId: skuData.ProductId,
SkuId: skuData.Id,
Ean: skuData.AlternateIds.Ean,
RefId: skuData.AlternateIds.RefId,
Name: `${skuData.ProductName} - ${skuData.SkuName}`,
ImageUrl: skuData.ImageUrl,
Categories: Object.values(skuData.ProductCategories).join(' - '),
BrandName: skuData.BrandName,
SalesChannels: skuData.SalesChannels.join(','),
CommercialCondition: skuData.CommercialConditionId,
}
}
}This script runs in the <head> of every page:
<script>
;(function () {
// Read settings injected by VTEX
let accountID = '{{settings.accountID}}'
let region = '{{settings.region}}'
let rawPrefs = '{{settings.preferences}}'
// Validate required fields
if (!accountID || !region) {
console.error('CleverTap: Missing configuration')
return
}
// Parse preferences
let preferences = JSON.parse(decodeURIComponent(rawPrefs))
// Build configuration object
const clevertapConfigs = {
accountID,
region,
isLogged: false,
privacy: { optOut: false, useIP: true },
spa: true,
preferences: {
allowUnknownUsersEvents: preferences.allowUnknownUsersEvents,
trackEvents: preferences.trackEvents,
},
}
// Store in localStorage for React app
localStorage.setItem('clevertapConfigs', JSON.stringify(clevertapConfigs))
})()
</script>Flow:
- VTEX injects settings as template variables
- Parse and validate configuration
- Store in localStorage
- React pixel app reads from localStorage on initialization
The app includes service workers to support push notifications and offline functionality.
function exampleSWHeader() {
importScripts(
'https://s3-eu-west-1.amazonaws.com/static.wizrocket.com/js/sw_webpush.js'
)
}
export default exampleSWHeaderThis imports CleverTap's service worker for push notification handling.
export function initClevertapNotifications() {
clevertap.notifications.push({
titleText: 'Would you like to receive Push Notifications?',
bodyText:
'We promise to only send you relevant content and give you updates on your transactions',
okButtonText: 'Sign me up!',
rejectButtonText: 'No thanks',
okButtonColor: '#F28046',
serviceWorkerPath: '/serviceWorkerMerged.js',
})
}Configuration Options:
titleText: Permission prompt titlebodyText: Permission prompt descriptionokButtonText: Accept button textrejectButtonText: Decline button textokButtonColor: Accept button colorserviceWorkerPath: Path to merged service worker
┌────────────────────────────────────────────────────────────┐
│ USER VISITS STORE (First Time) │
└──────────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CLEVERTAP PIXEL INITIALIZES │
│ (initClevertapNotifications called) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ PERMISSION PROMPT DISPLAYED │
│ "Would you like to receive Push Notifications?" │
│ [Sign me up!] [No thanks] │
└──────────────────────────┬─────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ User Accepts │ │ User Declines │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌──────────────────────┐
│ Register Service Worker│ │ No Notifications │
│ Request Browser Perm │ └──────────────────────┘
└────────┬───────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ BROWSER PERMISSION DIALOG │
│ "{domain} wants to show notifications" │
│ [Block] [Allow] │
└──────────────────────────┬──────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ User Allows │ │ User Blocks │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌──────────────────────┐
│ Generate Push Token │ │ Notifications │
│ Send to CleverTap │ │ Permanently Blocked │
└────────┬───────────────┘ └──────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ TOKEN STORED IN CLEVERTAP │
│ User Profile Updated with: │
│ - Push Token │
│ - Browser Info │
│ - Device Info │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ MARKETER CREATES CAMPAIGN IN CLEVERTAP │
│ - Select Segment │
│ - Compose Message │
│ - Schedule Send │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CLEVERTAP PROCESSES CAMPAIGN │
│ - Evaluates Segment │
│ - Prepares Push Payloads │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ CLEVERTAP SENDS TO BROWSER PUSH SERVICE │
│ (FCM for Chrome, APNs for Safari, etc.) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SERVICE WORKER RECEIVES PUSH EVENT │
│ (even if browser is closed) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────┐
│ NOTIFICATION DISPLAYED TO USER │
│ ┌──────────────────────────────────────────┐ │
│ │ [Icon] Your Store Name │ │
│ │ Product back in stock! │ │
│ │ The item you wanted is available now. │ │
│ │ │ │
│ │ [View Product] │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────┬─────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ User Clicks │ │ User Ignores │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌────────────────────────┐ ┌──────────────────────┐
│ Open Deep Link URL │ │ Event: Notification │
│ Track Click Event │ │ Received (no click) │
└────────────────────────┘ └──────────────────────┘
Symptoms:
- No events in CleverTap dashboard
- Console errors about CleverTap configuration
Diagnosis:
- Check browser console for errors
- Verify localStorage contains
clevertapConfigs - Check if CleverTap SDK is loaded
Solutions:
// In browser console:
localStorage.getItem('clevertapConfigs')
// Should return something like:
// {"accountID":"XXX-XXX-XXXX","region":"us1",...}
// If null or invalid:
// 1. Check app settings in VTEX Admin
// 2. Verify accountID and region are correct
// 3. Clear cache and reloadChecklist:
- App installed and configured in VTEX Admin
- CleverTap Project ID is correct
- CleverTap Region matches account
- Events are enabled in settings
- Browser allows third-party scripts
Symptoms:
- Storefront events work
- Checkout events (Checkout Started, Payment Info) not tracked
Diagnosis:
- Check if checkout script is installed
- Verify script URL is accessible
- Check browser console on checkout page
Solutions:
- Verify Script Installation:
// In checkout page console:
document.querySelector('script[src*="clevertap-checkout-events"]')
// Should return the script element
// If null, script is not installed-
Re-install Checkout Script:
- Go to Store Settings → Checkout → Code
- Add the script as per installation instructions
- Save and publish
-
Verify CDN Accessibility:
- Open:
https://cdn.jsdelivr.net/gh/GuiGiesbrecht/vtex-clevertap-scripts/clevertap-checkout-events.js - Should download/display JavaScript file
- If 404, contact support
- Open:
Symptoms:
- Catalog events not appearing
- Products not showing in CleverTap
Diagnosis:
- Check if catalog sync is enabled
- Verify integration email is set
- Check logs in VTEX IO
Solutions:
-
Enable Catalog Sync:
- Apps → My Apps → CleverTap
- Enable "Active Catalog Sync"
- Add valid Integration Email
- Save settings
-
Check Sync Status:
# In VTEX IO CLI:
vtex logs --all
# Look for messages like:
# "[OMS] 24h passed, running catalog sync..."
# "Processed: XXX skus"
# "Load completed"- Manual Trigger (if route enabled):
# POST request to catalog sync endpoint
curl -X POST 'https://{workspace}--{account}.myvtex.com/_v/catalog/sync' \
-H 'Content-Type: application/json' \
-d '{
"email": "integration@email.com",
"creator": "integration@email.com",
"replace": false
}'Symptoms:
- Events tracked but user data missing
- Anonymous users tracked correctly but logged-in users not identified
Diagnosis:
- Check if
vtex:userDataevent is firing - Verify Session API is accessible
- Check CleverTap profile in dashboard
Solutions:
- Verify Session Data:
// In browser console (while logged in):
fetch(
'/api/sessions?items=profile.firstName,profile.lastName,profile.email,profile.phone'
)
.then(r => r.json())
.then(console.log)
// Should return user profile data- Force Profile Update:
// In browser console:
const profile = {
Site: {
Name: 'Test User',
Email: 'test@email.com',
Phone: '+1234567890',
},
}
clevertap.onUserLogin.push(profile)- Check CleverTap Dashboard:
- Go to People → Search
- Search by email
- Verify profile properties are populated
Enable CleverTap debug logging:
// In browser console:
clevertap.setLogLevel(3) // 0=off, 1=error, 2=info, 3=debug
// View CleverTap internal state:
clevertap.getAccountID() // Should return your account ID
clevertap.getRegion() // Should return your regionView VTEX IO logs:
# Install VTEX CLI
npm install -g vtex
# Login
vtex login {account}
# View logs
vtex logs --all
# Filter for CleverTap app
vtex logs --all | grep clevertapKey Log Messages:
# Success:
"[OMS] Processing order: ORDER-123"
"[Catalog] Processed: 500 skus"
"[Catalog] Load completed"
# Errors:
"Error processing SKU 123: ..."
"CleverTap: failed to save config to localStorage"
"Error processing load: ..."
If experiencing high traffic:
-
Disable Unnecessary Events:
- Review tracking requirements
- Disable events not used in campaigns
- Example: Disable
Product Clickedif only usingProduct Viewed
-
Sample Anonymous Users:
// In enhancedEcommerceEvents.ts
const isUnknownEvents = verifyIsUnknownEvents()
const isLogged = verifyIsLogged()
// Add sampling for anonymous users
if (!isLogged && !isUnknownEvents) return
if (!isLogged && Math.random() > 0.5) return // Sample 50%- Batch Events (Advanced):
- Modify CleverTap SDK initialization
- Use offline mode and batch sends
- Reduce Sync Frequency:
// In omsFilteredEvents.ts
// Change from 24 hours to 48 hours:
if (hoursSince >= 48) {
// was 24
await catalogSyncHandler(ctx)
}- Filter Active SKUs Only:
// Already implemented in buildClevertapSkus:
if (!skuData.IsActive) continue- Increase Page Size:
// In CatalogService.ts
private pageSize = 50 // was 20-
Never Expose Passcode:
- Passcode only used in backend (Node.js)
- Never send to frontend
- Stored securely in app settings
-
Validate Input:
// In catalogSyncHandler.ts
if (!email || !email.includes('@')) {
throw new Error('Invalid email')
}
if (!accountName || accountName.length < 1) {
throw new Error('Invalid account name')
}CleverTap has rate limits. Implement safeguards:
// Track API calls
const apiCalls = new Map<string, number>()
function checkRateLimit(endpoint: string): boolean {
const count = apiCalls.get(endpoint) || 0
if (count > 100) {
// 100 calls per minute
return false
}
apiCalls.set(endpoint, count + 1)
return true
}# Check current version
vtex list | grep clevertap
# Update to latest version
vtex install clevertappartnerbr.vtex-clevertap-app@latest
# Or specific version
vtex install clevertappartnerbr.vtex-clevertap-app@0.0.20When upgrading, check for:
- New required settings
- Changed event names
- Updated API endpoints
- New permissions required
If issues occur after upgrade:
# Uninstall current version
vtex uninstall clevertappartnerbr.vtex-clevertap-app
# Install previous stable version
vtex install clevertappartnerbr.vtex-clevertap-app@0.0.19
# Verify functionality
# Test key events
# Check CleverTap dashboard| Event Name | VTEX Trigger | CleverTap Name | Key Properties |
|---|---|---|---|
vtex:search |
User performs search | Products Searched | query, results_count |
vtex:filterManipulation |
Filter applied | Product Filtered | filter_type, filter_value |
categoryView |
Category page view | Product List Viewed | category, products |
vtex:promoView |
Promotion displayed | Promotion Viewed | promo_id, promo_name |
vtex:promotionClick |
Promotion clicked | Promotion Clicked | promo_id, promo_name |
vtex:productView |
Product page view | Product Viewed | product_id, sku_id, name, price |
vtex:productClick |
Product clicked | Product Clicked | product_id, position, list |
vtex:addToCart |
Add to cart | Product Added to Cart | product_id, quantity, price |
vtex:removeFromCart |
Remove from cart | Product Removed from Cart | product_id, quantity |
vtex:viewCart |
Cart viewed | Cart Viewed | cart_total, items_count |
vtex:addToWishlist |
Add to wishlist | Product Added to Wishlist | product_id, sku_id |
vtex:removeToWishlist |
Remove from wishlist | Product Removed from Wishlist | product_id, sku_id |
vtex:share |
Product shared | Product Shared | product_id, channel |
vtex:orderPlaced |
Order placed | Order Created | order_id, value, items |
| Event Name | Trigger | CleverTap Name | Key Properties |
|---|---|---|---|
add_to_cart |
Add in checkout | Checkout Product Added To Cart | product_id, quantity |
remove_from_cart |
Remove in checkout | Checkout Product Removed From Cart | product_id, quantity |
view_cart |
Cart viewed in checkout | Checkout Cart Viewed | cart_total, items |
begin_checkout |
Checkout started | Checkout Started | cart_total, items_count |
add_payment_info |
Payment method selected | Payment Info | payment_method |
checkout_step_viewed |
Step viewed | Checkout Step Viewed | step, step_name |
coupon_applied |
Coupon success | Coupon Applied | coupon_code, discount |
coupon_denied |
Coupon failed | Coupon Denied | coupon_code, reason |
| OMS State | CleverTap Event | Description |
|---|---|---|
payment-approved |
Charged | Payment successful, order confirmed |
canceled |
Order Cancelled | Order cancelled by user or system |
incomplete |
Checkout Failed | Order incomplete or abandoned |
payment-denied |
Checkout Failed | Payment declined |
{
"title": "VTEX CleverTap Pixel App",
"type": "object",
"properties": {
"accountID": {
"title": "CleverTap Project ID",
"type": "string",
"minLength": 1,
"maxLength": 12
},
"accountPasscode": {
"title": "CleverTap Account Passcode",
"type": "string",
"minLength": 1,
"maxLength": 50
},
"region": {
"title": "CleverTap Region",
"type": "string",
"minLength": 1,
"maxLength": 4,
"enum": ["us1", "eu1", "in1", "sg1", "aps3", "mec1"]
},
"preferences": {
"type": "object",
"properties": {
"allowUnknownUsersEvents": {
"type": "boolean",
"default": true
},
"trackEvents": {
"type": "object",
"properties": {
"vtex:search": { "type": "boolean", "default": true },
"vtex:filterManipulation": { "type": "boolean", "default": true },
"categoryView": { "type": "boolean", "default": true },
"vtex:promoView": { "type": "boolean", "default": true },
"vtex:promotionClick": { "type": "boolean", "default": true },
"vtex:productView": { "type": "boolean", "default": true },
"vtex:productClick": { "type": "boolean", "default": true },
"vtex:addToCart": { "type": "boolean", "default": true },
"vtex:removeFromCart": { "type": "boolean", "default": true },
"vtex:viewCart": { "type": "boolean", "default": true },
"vtex:addToWishlist": { "type": "boolean", "default": true },
"vtex:removeToWishlist": { "type": "boolean", "default": true },
"vtex:share": { "type": "boolean", "default": true },
"vtex:orderPlaced": { "type": "boolean", "default": true },
"add_to_cart": { "type": "boolean", "default": true },
"remove_from_cart": { "type": "boolean", "default": true },
"view_cart": { "type": "boolean", "default": true },
"begin_checkout": { "type": "boolean", "default": true },
"add_payment_info": { "type": "boolean", "default": true },
"checkout_step_viewed": { "type": "boolean", "default": true },
"coupon_applied": { "type": "boolean", "default": true },
"coupon_denied": { "type": "boolean", "default": true }
}
},
"catalogSync": {
"type": "boolean",
"default": true
},
"integrationEmail": {
"type": "string",
"format": "email"
}
}
}
},
"required": ["accountID", "accountPasscode", "region"]
}| Endpoint | Method | Purpose |
|---|---|---|
/{region}.api.clevertap.com/1/upload |
POST | Upload events and profiles |
/{region}.api.clevertap.com/get_catalog_url |
POST | Get presigned S3 URL for catalog |
/{region}.api.clevertap.com/upload_catalog_completed |
POST | Notify catalog upload complete |
| API | Endpoint | Purpose |
|---|---|---|
| OMS | /api/oms/pvt/orders/{orderId} |
Get order details |
| Catalog | /api/catalog_system/pvt/sku/stockkeepingunitids |
Get SKU IDs |
| Catalog | /api/catalog_system/pvt/sku/stockkeepingunitbyid/{id} |
Get SKU details |
| Master Data | /api/dataentities/CL/search |
Search customer data |
| Session | /api/sessions |
Get user profile data |
- CleverTap Docs: https://docs.clevertap.com
- VTEX IO Docs: https://developers.vtex.com
- VTEX Pixel Docs: https://developers.vtex.com/docs/guides/vtex-io-documentation-pixel-apps
- VTEX Support: https://support.vtex.com
- CleverTap Support: https://support.clevertap.com
- GitHub Issues: Check app repository for issues
- CleverTap Event Naming: https://docs.clevertap.com/docs/events-naming
- VTEX IO Best Practices: https://developers.vtex.com/docs/guides/best-practices
- E-commerce Tracking Guide: https://docs.clevertap.com/docs/ecommerce-tracking
The VTEX CleverTap App provides a comprehensive, production-ready integration between VTEX and CleverTap platforms. This documentation covers all aspects of the application, from high-level architecture to detailed code implementations.
Key Takeaways:
- Easy Setup: Install from VTEX App Store, configure credentials, and add checkout script
- Automatic Tracking: Events automatically captured across storefront, checkout, and backend
- Catalog Sync: Products automatically synchronized every 24 hours
- Push Notifications: Full support for web push campaigns
- Customizable: Enable/disable events, configure sync frequency, and more
Next Steps:
- Install the app in your VTEX store
- Configure CleverTap credentials
- Install checkout script
- Verify events in CleverTap dashboard
- Create your first campaign
For questions or support, refer to the resources in Appendix D.
Version: 1.0 Last Updated: January 2026 Maintained by: CleverTap Partner BR