Web2Wave

Web2Wave Integration Guide

Integrate Botsi AI Pricing with Web2Wave to optimize web funnel pricing using Botsi's machine learning predictions. This guide covers how to create and configure all entities on both sides (Botsi and Web2Wave) and how to interact with Botsi's API using Web2Wave.

Web2Wave is a web-based subscription platform that handles quiz funnels, paywalls, and Stripe-based payments. The integration uses JavaScript scripts inserted into your Web2Wave quiz flow to communicate with Botsi's API, enabling AI-powered pricing optimization for your web funnels.

1. Webhook Setup

NOTE: This needs to be done only once for each product. Skip this step if you have already configured webhooks.

  1. Go to Web2Wave → Edit Project → API & Webhook tab → copy your webhook secret → update it in Botsi App Settings.
Web2Wave API and Webhook settings showing API key and webhook secret Botsi App Settings showing Web2Wave webhook configuration
  1. Copy the webhook URL from Botsi settings and update the webhook URL in your Web2Wave project settings under the API & Webhooks tab.
  2. Update the userProperties list in Web2Wave with these entries:
ip
screen_size
user_city_name
user_country_code
user_language
user_platform
user_state_name
user_zip_code
botsiProfileId
botsiPaywallId
botsiPlacementId
botsiIsExperiment
botsiAiPricingModelId
Web2Wave user properties configuration with Botsi fields

2. Product Setup

NOTE: This needs to be done only once for each product. Skip this step if you have already configured products.

  1. Create a Stripe product in your Stripe dashboard.
Stripe product creation dashboard
  1. Sync the Stripe product on Web2Wave so it's available during the paywall building process. This is under the Plans & Prices tab.
Web2Wave Plans and Prices tab showing Stripe product sync
  1. Create a Botsi product and specify the Stripe price ID as the web2wave_product_id.
Botsi product setup with Stripe price ID as web2wave_product_id

3. API Integration

The integration uses the Botsi WEB-API, authenticated with your app secret key. All client-side API calls are JavaScript scripts inserted into your Web2Wave quiz flow.

Important: The fields set using w2w.set() are sent in webhooks under userProperties. It is critical to set the values needed for Botsi to match data correctly:

w2w.set('botsiProfileId', data.data.profileId);
w2w.set('botsiPaywallId', data);
w2w.set('botsiPlacementId', data);
w2w.set('botsiIsExperiment', data);
w2w.set('botsiAiPricingModelId', data);

3a. Create Profile

Insert this script in your Web2Wave quiz flow to create a Botsi profile. The user_properties and user_id values are provided by Web2Wave. The user_id is used as customerUserId for user matching on the Botsi side.

(async function createBotsiProfile() {
  try {
    const p = window.user_properties || {};
    const u = window.user_id || "";

    // Replace with your actual Botsi secret key
    const apiKey = "key...";

    const payload = {
      customerUserId: u,
      country: p.user_country_code,
      platform: "stripe",
      locale: p.user_language,
      device: p.user_platform,
      os: /Mac OS X/.test(p.user_agent) ? "macos"
        : /Windows/.test(p.user_agent) ? "windows"
        : /iPhone|iPad/.test(p.user_agent) ? "ios"
        : /Android/.test(p.user_agent) ? "android"
        : "unknown",
      osVersion: "15.0.2",
      appVersion: "2.3.4",
      appBuild: "1.2.3"
    };

    const res = await fetch("https://app.botsi.com/api/v1/web-api/profiles", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Authorization": `${apiKey}`
      },
      body: JSON.stringify(payload)
    });

    if (!res.ok) throw new Error(`Botsi API error: ${res.status}`);
    const data = await res.json();

    // Set w2w property for webhooks validation
    if (data.ok && data.data?.profileId) {
      w2w.set('botsiProfileId', data.data.profileId);
    }
    return;
  } catch (err) {
    console.warn("Botsi profile creation failed silently:", err.message);
    return;
  }
})();

3b. Update Custom Attributes

Send custom user attributes to Botsi for improved LTV prediction. This includes web funnel-specific data like quiz answers, initial URL, IP address, and geographic information.

(async function syncBotsiProfile() {
  try {
    const p = window.user_properties || {};
    const u = window.user_id || "";

    // Replace with your actual Botsi secret key
    const apiKey = "key...";

    const payload = {
      customerUserId: u,
      custom: [
        { key: "has_app", value: p["has-app"] || "" },
        { key: "web2app_funnels", value: p["web2app-funnels"] || "" },
        { key: "initial_url", value: p.initial_url || "" },
        { key: "ip", value: p.ip || "" },
        { key: "user_state_name", value: p.user_state_name || "" },
        { key: "user_zip_code", value: p.user_zip_code || "" }
      ]
    };

    const res = await fetch(
      "https://app.botsi.com/api/v1/web-api/profiles/custom-attributes-all",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Accept": "application/json",
          "Authorization": `${apiKey}`
        },
        body: JSON.stringify(payload)
      }
    );

    if (!res.ok) throw new Error(`Botsi API error: ${res.status}`);
    const data = await res.json();

    if (data.profileId) {
      w2w.set('botsiProfileId', data.profileId);
    }
    return;
  } catch (err) {
    console.warn("Botsi sync failed silently:", err.message);
    return;
  }
})();

3c. Get Paywall

Fetch the AI-predicted paywall from Botsi. The returned externalId is used as the paywallSlug in Web2Wave to redirect to the correct paywall in the flow.

(async function fetchBotsiPaywall() {
  try {
    const p = window.user_properties || {};
    const u = window.user_id || "";
    const ua = p.user_agent || "";

    // Replace with your actual Botsi secret key
    const apiKey = "key...";

    const payload = {
      deviceTypeModel: p.user_platform || "desktop",
      osVersion: /Mac OS X/.test(ua) ? "macos"
        : /Windows/.test(ua) ? "windows"
        : /iPhone|iPad/.test(ua) ? "ios"
        : /Android/.test(ua) ? "android"
        : "unknown",
      languageLocale: p.user_language || "en",
      placementId: "OnboardingPlacement_ID123",
      attributionSource: "organic",
      profileId: p.botsiProfileId || "",
      store: "stripe"
    };

    const res = await fetch("https://app.botsi.com/api/v1/web-api/paywalls", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Authorization": `${apiKey}`
      },
      body: JSON.stringify(payload)
    });

    if (!res.ok) throw new Error(`Botsi API error: ${res.status}`);
    const data = await res.json();

    const paywallExternalId = data?.data?.externalId;
    const isExperiment = data?.data?.isExperiment ?? false;
    const placementId = data?.data?.placementId || "";
    const aiPricingModelId = data?.data?.aiPricingModelId || "";
    const paywallId = data?.data?.id || "";

    // Set paywallSlug for paywall redirect in the flow
    if (paywallExternalId) {
      w2w.set("paywallSlug", paywallExternalId);
    }

    // Store Botsi values for webhook parsing
    window.botsi_is_experiment = isExperiment;
    window.botsi_placement_id = placementId;
    window.botsi_ai_pricing_model_id = aiPricingModelId;
    window.botsi_paywall_id = paywallId;

    w2w.set("botsiIsExperiment", isExperiment);
    w2w.set("botsiPlacementId", placementId);
    w2w.set("botsiAiPricingModelId", aiPricingModelId);
    w2w.set("botsiPaywallId", paywallId);
    return;
  } catch (err) {
    console.warn("Botsi paywall fetch failed silently:", err.message);
    return;
  }
})();

3d. Create Event (Paywall Impression)

Send a paywall impression event to Botsi when the paywall is shown to the user. This is critical for Botsi's AI model to learn and improve predictions.

(async function insertUserEvent() {
  const p = window.user_properties || {};

  // Replace with your actual Botsi secret key
  const apiKey = "key...";

  try {
    const res = await fetch("https://app.botsi.com/api/v1/web-api/events", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "Authorization": `${apiKey}`
      },
      body: JSON.stringify([
        {
          type: "paywall_shown",
          profileId: p.botsi_profile_id || "",
          paywallId: p.botsiPaywallId || "",
          placementId: p.botsiPlacementId || "",
          isExperiment: p.botsiIsExperiment ?? false,
          aiPricingModelId: p.botsiAiPricingModelId || ""
        }
      ])
    });

    if (!res.ok) throw new Error(`Botsi API error: ${res.status}`);
    const data = await res.json();
  } catch (err) {
    console.warn("Botsi paywall shown event failed silently:", err.message);
  }
})();

4. Web2Wave Flow Setup

After configuring the API scripts above, set up your Web2Wave quiz flow to include these scripts at the appropriate steps:

  1. Quiz Start: Insert the createBotsiProfile script to create a Botsi profile when the user begins the funnel.
  2. After Quiz Completion: Insert the syncBotsiProfile script to send custom attributes collected during the quiz.
  3. Before Paywall: Insert the fetchBotsiPaywall script to get the AI-predicted paywall. Use the paywallSlug value to redirect the user to the correct paywall page in your Web2Wave flow.
  4. Paywall Display: Insert the insertUserEvent script to send the paywall impression event.
  5. Purchase: Web2Wave and Stripe handle the purchase processing. Botsi receives the transaction data via the webhook configured in Step 1.

Notes

  • Web funnel events and custom profile data are critical for Botsi's LTV prediction model — send as many relevant user attributes as possible.
  • The w2w.set() values are transmitted via webhooks under userProperties, so ensure all Botsi-related fields are set before the paywall is shown.
  • The paywallSlug (from externalId) is used by Web2Wave to route the user to the correct paywall page within the flow.
  • Replace "key..." with your actual Botsi app secret key in all scripts.
  • The store value should always be "stripe" for Web2Wave integrations.
  • Test the integration in a staging environment before deploying to production.
  • Ensure your Stripe webhook is correctly configured in both Web2Wave and Botsi for purchase validation.