Skip to main content

Conversion tracking

Selektable can attribute conversions to widget visualizations, so you can measure whether customers who use the widget are more likely to take action. A conversion can be a purchase, a form submission, an appointment booking, or any other goal. For WooCommerce and Shopify stores, conversion tracking is handled automatically via platform webhooks. This guide covers custom sites that need to send conversion data manually.

How it works

  1. The embed script assigns each visitor a persistent visitor ID (stored in localStorage) and a session ID (stored in sessionStorage)
  2. When a customer generates a visualization, it’s recorded with their visitor and session IDs
  3. When a conversion happens (purchase, form submission, etc.), your server sends a webhook to Selektable that includes the same IDs
  4. Selektable’s attribution engine matches the conversion to any recent visualizations within the attribution window (default: 7 days)

Prerequisites

  • The Selektable embed script must be loaded on your site, including pages with forms or checkout
  • You need your store ID, organization ID, and API key from the dashboard
API keys start with sel_ and are used as a Bearer token in the Authorization header of every webhook request.
Your API key is a secret. Never expose it in client-side JavaScript. The webhook must always be sent from your server.

How tracking IDs reach your server

Selektable.getIdentity() is a client-side browser API that reads from localStorage and sessionStorage. It is not available on your server. To send the webhook from your backend, you need to pass the tracking IDs from the browser through your form using hidden fields. Add these to any form where you want to track conversions:
<form action="/your-endpoint" method="POST">
  <!-- Your form fields here -->

  <!-- Hidden tracking fields (populated by JavaScript) -->
  <input type="hidden" name="visitorId" id="sel-visitor-id" />
  <input type="hidden" name="sessionId" id="sel-session-id" />

  <button type="submit">Submit</button>
</form>

<script>
  var identity = Selektable.getIdentity();
  document.getElementById('sel-visitor-id').value = identity.visitorId;
  document.getElementById('sel-session-id').value = identity.sessionId;
</script>
Your server then reads visitorId and sessionId from the form submission and includes them in the webhook payload.

Ecommerce order tracking

Track purchases by sending the order details along with the visitor’s tracking IDs from your server. Step 1: Add hidden tracking fields to your checkout form (see above). Step 2: When the order is placed, send the webhook from your server.
app.post('/checkout/complete', async (req, res) => {
  const { visitorId, sessionId, order } = req.body;

  await fetch('https://app.selektable.com/api/webhooks/orders', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer sel_your_api_key'
    },
    body: JSON.stringify({
      orderId: order.id,
      storeId: 'store_xxx',
      organizationId: 'org_xxx',
      visitorId: visitorId,
      sessionId: sessionId,
      totalCents: order.totalCents,
      currency: order.currency,
      platform: 'custom',
      status: 'completed',
      orderCreatedAt: new Date().toISOString(),
      items: order.items.map(item => ({
        productId: item.productId,
        quantity: item.quantity,
        priceCents: item.priceCents,
        totalCents: item.priceCents * item.quantity
      }))
    })
  });

  res.redirect('/thank-you');
});
The productId in your items should match the productId you pass to Selektable.open() on your product pages. This is how Selektable links a purchase to a specific product visualization.

Lead gen / appointment tracking

If your site uses a contact form, appointment booking, or any non-ecommerce conversion, you can track form submissions as conversions. Set totalCents to 0 and pass an empty items array. Step 1: Add hidden tracking fields to your form.
<form action="/appointment/book" method="POST">
  <input type="text" name="name" placeholder="Name" />
  <input type="email" name="email" placeholder="Email" />
  <input type="tel" name="phone" placeholder="Phone" />

  <!-- Hidden tracking fields (populated by JavaScript) -->
  <input type="hidden" name="visitorId" id="sel-visitor-id" />
  <input type="hidden" name="sessionId" id="sel-session-id" />

  <button type="submit">Book appointment</button>
</form>

<script>
  var identity = Selektable.getIdentity();
  document.getElementById('sel-visitor-id').value = identity.visitorId;
  document.getElementById('sel-session-id').value = identity.sessionId;
</script>
Step 2: On your server, read the tracking IDs from the form submission and send the webhook.
app.post('/appointment/book', async (req, res) => {
  const { visitorId, sessionId, name, email, phone } = req.body;

  // Save the appointment in your system
  const appointment = await saveAppointment({ name, email, phone });

  // Track the conversion in Selektable
  await fetch('https://app.selektable.com/api/webhooks/orders', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer sel_your_api_key'
    },
    body: JSON.stringify({
      orderId: 'appt-' + appointment.id,
      storeId: 'store_xxx',
      organizationId: 'org_xxx',
      visitorId: visitorId,
      sessionId: sessionId,
      totalCents: 0,
      currency: 'USD',
      platform: 'custom',
      status: 'completed',
      orderCreatedAt: new Date().toISOString(),
      items: []
    })
  });

  res.redirect('/thank-you');
});

Webhook payload reference

POST https://app.selektable.com/api/webhooks/orders
Authorization: Bearer sel_your_api_key
Content-Type: application/json
FieldTypeRequiredDescription
orderIdstringYesUnique identifier for this conversion (order ID, form submission ID, etc.)
storeIdstringYesYour Selektable store ID
organizationIdstringYesYour Selektable organization ID
visitorIdstringYesFrom Selektable.getIdentity().visitorId on the client, passed to your server via hidden form fields
sessionIdstringYesFrom Selektable.getIdentity().sessionId on the client, passed to your server via hidden form fields
totalCentsintegerYesOrder total in cents, or 0 for lead gen
currencystringYes3-letter currency code (e.g., USD, EUR)
platformstringYesMust be "custom"
statusstringYesOne of: pending, processing, on-hold, completed, refunded, cancelled
orderCreatedAtstringYesISO 8601 date string
itemsarrayYesArray of order items, or [] for lead gen
Order item fields (for ecommerce):
FieldTypeRequiredDescription
productIdstringYesMust match the productId passed to Selektable.open()
quantityintegerYesQuantity purchased
priceCentsintegerYesUnit price in cents
totalCentsintegerYesLine total in cents (priceCents * quantity)

Viewing conversion data

Once conversions are tracked, analytics appear in your dashboard:
  1. Go to the Selektable dashboard
  2. Open your store and select a widget
  3. Navigate to the Analytics tab
You’ll see:
  • Recent conversions with IDs and attributed revenue (or $0 for leads)
  • Conversion type (direct or assisted)
  • Time to conversion showing how long after a visualization the action occurred

Troubleshooting

Make sure you’re including the Authorization header with a valid API key. The format is Bearer sel_your_api_key. You can find your API key in the dashboard.
Verify that:
  • The Authorization header includes a valid API key.
  • The visitorId and sessionId are not empty. Log Selektable.getIdentity() on the client to check the values before form submission.
  • The storeId and organizationId match your dashboard values.
  • The webhook returns { "success": true }.
The visitor ID is stored in localStorage on the same domain. If your form redirects to a different domain (e.g., a third-party payment page or booking tool), the visitor ID won’t carry over. Make sure the form page is on the same domain as your product pages.
The session ID uses sessionStorage, which resets when a new tab opens. If your checkout or form flow opens a new tab or window, the session ID will be different. The visitor ID (which persists across sessions) is more reliable for attribution in these cases.

Next steps