Custom Integrations#

Comprehensive guide to integrate Pulsora with any platform, CRM, or custom system. Covers Shopify, WordPress, Salesforce, HubSpot, manual invoicing, headless CMS, mobile apps, and custom payment processors.

1. Initialize Pulsora in the Frontend#

Every integration starts by installing @pulsora/core (or the CDN snippet) and capturing the two attribution values you need later:

import { Pulsora } from '@pulsora/core';

const pulsora = new Pulsora();
pulsora.init({ apiToken: 'pub_123' });

async function getAttribution() {
  return {
    visitor_fingerprint: await pulsora.getVisitorFingerprint(),
    session_id: pulsora.getSessionId(),
  };
}

2. Forward Attribution to Your Backend#

Whenever a conversion flow starts, send the fingerprint + session to your server. Examples:

  • Form submission API payload
  • Hidden fields inside a classic HTML form
  • Custom SDK metadata (Stripe, Paddle, Chargebee, LemonSqueezy, Shopify, etc.)
  • Native mobile bridge that posts to your backend
const attribution = await getAttribution();

await fetch('/api/lead', {
  method: 'POST',
  body: JSON.stringify({
    ...leadData,
    ...attribution,
  }),
});

3. Emit Revenue or Lifecycle Events#

Use @pulsora/revenue anywhere you finalize payments, invoices, or offline deals:

import { Revenue } from '@pulsora/revenue';

const revenue = new Revenue({ apiToken: process.env.PULSORA_SECRET! });

async function recordDeal(payload: DealPayload) {
  await revenue.track({
    visitor_fingerprint: payload.visitor_fingerprint ?? 'manual',
    session_id: payload.session_id ?? 'manual',
    customer_id: payload.customer_id,
    amount: payload.amount,
    currency: payload.currency ?? 'USD',
    transaction_id: payload.reference,
    payment_processor: payload.source ?? 'internal',
    event_type: payload.type ?? 'purchase',
    metadata: payload.metadata,
  });
}

If you ingest revenue from third-party CRMs (Salesforce, HubSpot), map their lifecycle changes to Pulsora event types.

4. Handle Failures & Retries#

  • Wrap revenue.track() in try/catch and log to your monitoring system.
  • Add idempotency by reusing transaction_id—Pulsora deduplicates identical IDs.
  • Queue retries when your downstream systems are unavailable (BullMQ, SQS, etc.).

5. Server-Side Event Tracking#

If you need to record additional server-side analytics (e.g. API usage), send raw requests to POST /api/ingest:

await fetch('https://pulsora.co/api/ingest', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Token': process.env.PULSORA_SECRET!,
  },
  body: JSON.stringify({
    type: 'event',
    data: {
      visitor_fingerprint: fingerprint,
      session_id: sessionId,
      event_name: 'api_call',
      event_data: { endpoint: '/v1/exports' },
      timestamp: Date.now(),
    },
  }),
});

This mirrors what the core SDK sends from the browser.

6. Privacy & Compliance#

Custom integrations must respect Pulsora’s privacy guarantees:

  • Do not send PII in event names or metadata (use IDs instead of emails).
  • Hash customer IDs on your backend if required by policy.

Platform-Specific Examples#

Shopify Integration#

Track Shopify orders with full visitor attribution:

<!-- theme.liquid - Add Pulsora to all pages -->
<script src="https://cdn.pulsora.co/v1/pulsora.min.js" data-token="pub_xxx"></script>

<!-- checkout.liquid or additional scripts -->
<script>
  // Capture attribution and store in session
  async function captureAttribution() {
    const fingerprint = await window.pulsora.getVisitorFingerprint();
    const sessionId = window.pulsora.getSessionId();

    // Store in sessionStorage for checkout
    sessionStorage.setItem('pulsora_fingerprint', fingerprint);
    sessionStorage.setItem('pulsora_session', sessionId);
  }

  // Run on page load
  window.addEventListener('load', captureAttribution);
</script>

Backend webhook handler (Node.js):

// webhooks/shopify-orders.js
import { Revenue } from '@pulsora/revenue';
import crypto from 'crypto';

const revenue = new Revenue({ apiToken: process.env.PULSORA_SECRET! });

// Verify Shopify HMAC
function verifyShopifyWebhook(data, hmacHeader) {
  const hash = crypto
    .createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SECRET!)
    .update(data, 'utf8')
    .digest('base64');
  return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(hmacHeader));
}

export async function POST(req: Request) {
  const body = await req.text();
  const hmac = req.headers.get('X-Shopify-Hmac-SHA256');

  if (!verifyShopifyWebhook(body, hmac)) {
    return Response.json({ error: 'Invalid signature' }, { status: 401 });
  }

  const order = JSON.parse(body);

  // Get attribution from order notes or metafields
  const fingerprint = order.note_attributes?.find(
    attr => attr.name === 'pulsora_fingerprint'
  )?.value;

  const sessionId = order.note_attributes?.find(
    attr => attr.name === 'pulsora_session'
  )?.value;

  await revenue.track({
    visitor_fingerprint: fingerprint,
    session_id: sessionId,
    customer_id: order.customer.id.toString(),
    amount: parseFloat(order.total_price),
    currency: order.currency,
    transaction_id: order.id.toString(),
    payment_processor: 'shopify',
    event_type: 'purchase',
    metadata: {
      order_number: order.order_number,
      items_count: order.line_items.length,
      discount_codes: order.discount_codes.map(d => d.code).join(',')
    }
  });

  return Response.json({ success: true });
}

WordPress / WooCommerce#

Add Pulsora to WordPress theme:

<!-- functions.php -->
<?php
function add_pulsora_analytics() {
    ?>
    <script src="https://cdn.pulsora.co/v1/pulsora.min.js" data-token="pub_xxx"></script>
    <script>
        // Store attribution in WordPress session
        window.addEventListener('load', async function() {
            const fingerprint = await window.pulsora.getVisitorFingerprint();
            const sessionId = window.pulsora.getSessionId();

            // Send to WordPress backend via AJAX
            jQuery.post(ajaxurl, {
                action: 'store_pulsora_attribution',
                fingerprint: fingerprint,
                session_id: sessionId,
                nonce: '<?php echo wp_create_nonce("pulsora_nonce"); ?>'
            });
        });
    </script>
    <?php
}
add_action('wp_footer', 'add_pulsora_analytics');

// Store in WooCommerce order
add_action('woocommerce_checkout_create_order', function($order, $data) {
    if (isset($_SESSION['pulsora_fingerprint'])) {
        $order->update_meta_data('_pulsora_fingerprint', $_SESSION['pulsora_fingerprint']);
        $order->update_meta_data('_pulsora_session', $_SESSION['pulsora_session']);
    }
}, 10, 2);

// Track completed orders
add_action('woocommerce_payment_complete', function($order_id) {
    $order = wc_get_order($order_id);

    $data = [
        'visitor_fingerprint' => $order->get_meta('_pulsora_fingerprint'),
        'session_id' => $order->get_meta('_pulsora_session'),
        'customer_id' => $order->get_customer_id(),
        'amount' => $order->get_total(),
        'currency' => $order->get_currency(),
        'transaction_id' => $order_id,
        'payment_processor' => 'woocommerce'
    ];

    // Send to your backend that uses @pulsora/revenue
    wp_remote_post('https://yourbackend.com/api/track-revenue', [
        'body' => json_encode($data),
        'headers' => ['Content-Type' => 'application/json']
    ]);
});

Salesforce Integration#

Sync Salesforce opportunities with Pulsora:

// services/salesforce-sync.ts
import { Revenue } from '@pulsora/revenue';
import jsforce from 'jsforce';

const revenue = new Revenue({ apiToken: process.env.PULSORA_SECRET! });
const sf = new jsforce.Connection({
  loginUrl: 'https://login.salesforce.com',
});

await sf.login(process.env.SF_USERNAME!, process.env.SF_PASSWORD!);

// Track opportunity closed-won
async function trackClosedOpportunity(opportunityId: string) {
  const opp = await sf.sobject('Opportunity').retrieve(opportunityId);

  // Get attribution from custom fields
  const fingerprint = opp.Pulsora_Fingerprint__c;
  const sessionId = opp.Pulsora_Session__c;

  await revenue.track({
    visitor_fingerprint: fingerprint,
    session_id: sessionId,
    customer_id: opp.AccountId,
    amount: opp.Amount,
    currency: opp.CurrencyIsoCode || 'USD',
    transaction_id: opp.Id,
    payment_processor: 'salesforce',
    event_type: 'purchase',
    customer_subscription_id:
      opp.Type === 'Renewal' ? opp.AccountId : undefined,
    is_recurring: opp.Type === 'New Business' || opp.Type === 'Renewal',
    mrr_impact: opp.Type === 'New Business' ? opp.Amount / 12 : 0,
    metadata: {
      opportunity_name: opp.Name,
      stage: opp.StageName,
      lead_source: opp.LeadSource,
      close_date: opp.CloseDate,
    },
  });
}

// Listen for Opportunity updates via Salesforce Platform Events
sf.streaming.topic('OpportunityUpdates').subscribe(async (message) => {
  if (message.sobject.StageName === 'Closed Won') {
    await trackClosedOpportunity(message.sobject.Id);
  }
});

HubSpot Integration#

// services/hubspot-sync.ts
import { Revenue } from '@pulsora/revenue';
import { Client } from '@hubspot/api-client';

const revenue = new Revenue({ apiToken: process.env.PULSORA_SECRET! });
const hubspot = new Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN! });

// Webhook handler for deal updates
export async function handleHubSpotWebhook(req: Request) {
  const payload = await req.json();

  for (const event of payload) {
    if (
      event.subscriptionType === 'deal.propertyChange' &&
      event.propertyName === 'dealstage' &&
      event.propertyValue === 'closedwon'
    ) {
      const dealId = event.objectId;

      // Get deal details
      const deal = await hubspot.crm.deals.basicApi.getById(dealId, [
        'amount',
        'dealname',
        'pulsora_fingerprint', // Custom property
        'pulsora_session', // Custom property
        'closedate',
      ]);

      await revenue.track({
        visitor_fingerprint: deal.properties.pulsora_fingerprint,
        session_id: deal.properties.pulsora_session,
        customer_id: deal.associations?.companies?.[0]?.id.toString(),
        amount: parseFloat(deal.properties.amount),
        currency: 'USD',
        transaction_id: dealId,
        payment_processor: 'hubspot',
        event_type: 'purchase',
        metadata: {
          deal_name: deal.properties.dealname,
          close_date: deal.properties.closedate,
        },
      });
    }
  }

  return Response.json({ success: true });
}

Manual Invoice Tracking#

For businesses with manual invoicing:

// api/invoices/route.ts
import { Revenue } from '@pulsora/revenue';

const revenue = new Revenue({ apiToken: process.env.PULSORA_SECRET! });

export async function POST(req: Request) {
  const invoice = await req.json();

  // Track manual invoice
  await revenue.track({
    // Attribution may be null for manual deals
    visitor_fingerprint: invoice.attribution?.fingerprint || 'manual',
    session_id: invoice.attribution?.session || 'manual',
    customer_id: invoice.customer_id,
    amount: invoice.amount,
    currency: invoice.currency || 'USD',
    transaction_id: invoice.invoice_number,
    payment_processor: 'manual',
    event_type: 'purchase',
    metadata: {
      invoice_number: invoice.invoice_number,
      payment_method: invoice.payment_method,
      terms: invoice.payment_terms,
      issued_date: invoice.issued_date,
      due_date: invoice.due_date,
      notes: invoice.notes,
    },
  });

  return Response.json({ success: true });
}

// Usage from admin panel
const response = await fetch('/api/invoices', {
  method: 'POST',
  body: JSON.stringify({
    customer_id: 'cust_123',
    amount: 5000,
    currency: 'USD',
    invoice_number: 'INV-2024-001',
    payment_method: 'Bank Transfer',
    payment_terms: 'Net 30',
    issued_date: '2024-01-15',
    due_date: '2024-02-14',
    notes: 'Annual enterprise contract',
  }),
});

Headless CMS (Contentful, Sanity)#

// Track content conversions (e.g., gated content downloads)
import { Pulsora } from '@pulsora/core';

const pulsora = new Pulsora();
pulsora.init({ apiToken: 'pub_xxx' });

async function trackContentDownload(contentId: string, contentTitle: string) {
  // Get attribution
  const fingerprint = await pulsora.getVisitorFingerprint();
  const sessionId = pulsora.getSessionId();

  // Track client-side event
  await pulsora.event('content_download', {
    content_id: contentId,
    content_title: contentTitle,
  });

  // Send to backend for lead scoring
  await fetch('/api/leads/content-download', {
    method: 'POST',
    body: JSON.stringify({
      visitor_fingerprint: fingerprint,
      session_id: sessionId,
      content_id: contentId,
      content_title: contentTitle,
      email: userEmail, // If collected
    }),
  });
}

// Track form submissions
document.querySelector('form').addEventListener('submit', async (e) => {
  e.preventDefault();

  const fingerprint = await pulsora.getVisitorFingerprint();
  const sessionId = pulsora.getSessionId();

  const formData = new FormData(e.target);

  await fetch('/api/leads', {
    method: 'POST',
    body: JSON.stringify({
      ...Object.fromEntries(formData),
      visitor_fingerprint: fingerprint,
      session_id: sessionId,
    }),
  });
});

Mobile App (React Native)#

// services/analytics.ts
import { Pulsora } from '@pulsora/core';
import AsyncStorage from '@react-native-async-storage/async-storage';

class MobileAnalytics {
  private pulsora: Pulsora;

  constructor() {
    this.pulsora = new Pulsora();
    this.init();
  }

  async init() {
    await this.pulsora.init({
      apiToken: 'pub_xxx',
    });
  }

  async trackPurchase(purchase: {
    productId: string;
    amount: number;
    currency: string;
  }) {
    const fingerprint = await this.pulsora.getVisitorFingerprint();
    const sessionId = this.pulsora.getSessionId();

    // Send to backend for revenue tracking
    await fetch('https://api.yourapp.com/revenue', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        visitor_fingerprint: fingerprint,
        session_id: sessionId,
        ...purchase,
      }),
    });
  }
}

export const analytics = new MobileAnalytics();

// Usage in purchase flow
import { analytics } from './services/analytics';

async function handlePurchase(productId: string) {
  // Process payment with your payment provider
  const payment = await processPayment(productId);

  // Track with Pulsora
  await analytics.trackPurchase({
    productId,
    amount: payment.amount,
    currency: payment.currency,
  });
}

Troubleshooting#

Attribution Data Not Captured#

Problem: Fingerprint/session not available during checkout

Solutions:

  1. Store in sessionStorage:
// On page load
const fingerprint = await pulsora.getVisitorFingerprint();
sessionStorage.setItem('pulsora_fp', fingerprint);
sessionStorage.setItem('pulsora_sid', pulsora.getSessionId());

// During checkout
const fp = sessionStorage.getItem('pulsora_fp');
const sid = sessionStorage.getItem('pulsora_sid');
  1. Use cookies (GDPR-compliant):
// Store attribution in first-party cookie
document.cookie = `pulsora_fp=${fingerprint}; path=/; max-age=1800; secure; samesite=strict`;
  1. Hidden form fields:
<form action="/checkout" method="POST">
  <input type="hidden" name="visitor_fingerprint" id="fp" />
  <input type="hidden" name="session_id" id="sid" />
  <!-- Other fields -->
</form>

<script>
  window.addEventListener('load', async () => {
    const fp = await window.pulsora.getVisitorFingerprint();
    const sid = window.pulsora.getSessionId();
    document.getElementById('fp').value = fp;
    document.getElementById('sid').value = sid;
  });
</script>

CRM Sync Issues#

Problem: Opportunities/deals not syncing to Pulsora

Debugging:

// Add detailed logging
async function syncDeal(dealId: string) {
  console.log('[CRM Sync] Starting:', dealId);

  try {
    const deal = await fetchDealFromCRM(dealId);
    console.log('[CRM Sync] Deal data:', deal);

    const result = await revenue.track({
      visitor_fingerprint: deal.fingerprint || 'crm-manual',
      session_id: deal.session || 'crm-manual',
      customer_id: deal.customer_id,
      amount: deal.amount,
      currency: deal.currency,
      transaction_id: dealId,
      payment_processor: 'crm',
      event_type: 'purchase',
    });

    console.log('[CRM Sync] Success:', result);
  } catch (error) {
    console.error('[CRM Sync] Error:', error);

    // Retry with exponential backoff
    await retryWithBackoff(() => syncDeal(dealId));
  }
}

Manual Revenue Not Attributed#

Issue: Manual deals show "Unknown source"

Solution:

For offline/manual deals without attribution, track as manual:

await revenue.track({
  visitor_fingerprint: 'manual-entry',
  session_id: 'manual-entry',
  customer_id: customerId,
  amount: amount,
  currency: 'USD',
  transaction_id: invoiceNumber,
  payment_processor: 'manual',
  event_type: 'purchase',
  metadata: {
    source: 'manual_invoice',
    sales_rep: salesRepId,
    notes: 'Enterprise deal closed offline',
  },
});

Then segment manual vs attributed revenue in your dashboard.

Architecture Checklist#

  • ✅ Capture fingerprint + session in every conversion flow
  • ✅ Store attribution alongside the transaction in your database
  • ✅ Send revenue events with the same transaction ID for deduplication
  • ✅ Retry failures with exponential backoff
  • ✅ Monitor webhook endpoints (5xx responses, timeouts)
  • ✅ Handle null/missing attribution gracefully
  • ✅ Log all CRM/platform sync operations
  • ✅ Test with real transactions in sandbox mode
  • ✅ Document custom fields in CRM for attribution data
  • ✅ Set up alerts for sync failures

Next Steps#