# getVisitorData()

Returns data about the current visitor and their A/B test assignments. This is a **synchronous** method — it returns data immediately without a Promise.

The primary use case is sending Shoplift test data to third-party analytics platforms so you can analyze test results alongside your existing metrics.

### Signature

```typescript
window.shoplift.getVisitorData(): ExternalVisitorData
```

### Parameters

None.

### Returns

`ExternalVisitorData` — an object containing two properties:

```typescript
interface ExternalVisitorData {
  visitor: Visitor | null;
  visitorTests: ExternalTestRelation[];
}
```

> **Note:** This is a synchronous call (not a Promise). No `await` needed. Invalid test assignments are automatically filtered out, and internal tracking fields are excluded.

#### `visitor`

Information about the current visitor. Returns `null` if the visitor hasn't been synced with the server yet (typically because this is the visitor's first page load and analytics consent hasn't been granted). Once populated, the visitor object persists in memory for the remainder of the session.

```typescript
interface Visitor {
  id: string;                        // Unique Shoplift visitor ID (UUID)
  storedAt: Date;                    // When visitor record was last stored
  createdAt: Date;                   // When visitor first arrived
  shopifyAnalyticsId: string | null; // Shopify's analytics ID (if available)
  device: 'desktop' | 'mobile';     // Device type
  country: string | null;           // Visitor country (if available)
  utmSource: string;                // UTM source parameter
  utmMedium: string;                // UTM medium parameter
  utmCampaign: string;              // UTM campaign parameter
  utmContent: string;               // UTM content parameter
  referrer: string;                 // Original referring URL
}
```

#### `visitorTests`

An array of test assignments for the current visitor. Each entry represents one test the visitor is participating in. Internal tracking fields are removed before returning.

```typescript
interface ExternalTestRelation {
  createdAt: Date;                  // When assigned to test
  testId: string;                   // Test identifier
  hypothesisId: string | null;      // Assigned hypothesis ID
  isThemeTest: boolean;             // Whether this is a theme test
  themeId?: number;                 // Theme ID (for theme tests only)
  isSaved: boolean;                 // Whether assignment was saved to server
  isInvalid?: boolean;              // Whether assignment has been invalidated
}
```

In practice, every entry in the returned array will have a non-null `hypothesisId` — blocked and excluded visitors are filtered out automatically.

### Basic Usage

```javascript
const data = window.shoplift.getVisitorData();

if (data.visitor) {
  console.log('Visitor ID:', data.visitor.id);
  console.log('Device:', data.visitor.device);
  console.log('Country:', data.visitor.country);
}

data.visitorTests.forEach((test) => {
  console.log(`Test ${test.testId}: assigned to ${test.hypothesisId}`);
});
```

### Common Patterns

#### Send Test Data to an Analytics Platform

Retrieve visitor data and forward it to your analytics tool:

```javascript
function sendShopliftDataToAnalytics() {
  if (!window.shoplift) return;

  const { visitor, visitorTests } = window.shoplift.getVisitorData();
  if (!visitor || visitorTests.length === 0) return;

  visitorTests.forEach((test) => {
    // Replace with your analytics platform's API
    trackEvent('Experiment Viewed', {
      experiment_id: test.testId,
      variant_id: test.hypothesisId,
      visitor_id: visitor.id,
      device: visitor.device,
      country: visitor.country,
    });
  });
}

// Wait for Shoplift, then send data
function onShopliftReady(callback, maxAttempts = 50) {
  let attempts = 0;

  function check() {
    if (window.shoplift) {
      callback();
    } else if (attempts < maxAttempts) {
      attempts++;
      setTimeout(check, 100);
    }
  }

  check();
}

onShopliftReady(sendShopliftDataToAnalytics);
```

For platform-specific implementations, see the integration guides for Segment, Heap, Hotjar, Microsoft Clarity, and Adobe Analytics.

#### Debugging in the Console

Quick way to inspect the current visitor state:

```javascript
// Dump all visitor data
console.table(window.shoplift.getVisitorData().visitor);

// List all active test assignments
window.shoplift.getVisitorData().visitorTests.forEach((t) => {
  console.log(`${t.testId} → ${t.hypothesisId} (${t.isThemeTest ? 'theme' : 'element'})`);
});
```

#### Conditional Logic Based on Test Participation

Check whether a visitor is in any test before running custom logic:

```javascript
const { visitorTests } = window.shoplift.getVisitorData();

if (visitorTests.length > 0) {
  console.log(`Active in ${visitorTests.length} test(s)`);
}

// Check for a specific test
const myTest = visitorTests.find((t) => t.testId === 'specific-test-id');
if (myTest) {
  console.log('Assigned hypothesis:', myTest.hypothesisId);
}
```

### Edge Cases

* **Visitor is `null`**: The visitor object is `null` until Shoplift has synced with the server, which requires analytics consent. On return visits, the visitor may be rehydrated from stored state. In preview mode (`?isShopliftMerchant=true`), visitor is always `null`.
* **Invalid assignments filtered**: Internally invalid test assignments (marked `isInvalid`) are automatically excluded from the returned array — you don't need to filter them yourself.
* **No active tests**: If the visitor isn't participating in any tests, `visitorTests` will be an empty array. `visitor` data may still be available.
* **UTM parameters**: UTM fields are populated from the visitor's landing page URL. They'll be empty strings if no UTM parameters were present.
* **Country detection**: The `country` field uses GeoIP and may be `null` if the visitor's location couldn't be determined.
