# SDK API Reference

Methods on the global optimo object — track, identify, get variants, consent.

The Otter SDK exposes a global `optimo` object you can call from anywhere on the page. Use it to track events, identify users, look up variant assignments, and manage consent.

Most of the SDK is invisible — variant assignment, DOM application, conversion tracking for click and pageview goals all happen automatically. The methods below are what you reach for when you need to drive the SDK from your own code: tracking custom events, attributing revenue, identifying logged-in users for cross-device assignment, and reading the current variant so you can branch your application logic.

The object lives at `window.optimo` once the SDK has loaded. Since the script is async, you can queue calls before the SDK is ready by pushing onto the queue at `window.optimoq` — the install snippet initializes that array for you, and the SDK replays the queue once it's initialized.

## Track Events

```js
// Track a custom event
optimo.track("signup_complete", {
  plan: "pro",
  source: "landing_page"
});

// Track revenue
optimo.revenue({
  value: 49.99,
  currency: "USD",
  order_id: "ORDER-1234" // optional: counts this order only once
});
```

## Identify Users

```js
// Set a known user ID before identifying traits
optimo.userID("user_12345");

// Identify a visitor with properties
optimo.identify({
  email: "user@example.com",
  name: "Jane Doe",
  plan: "pro"
});
```

## Get Variants

```js
// Get the active variant for a test
const variant = optimo.getVariant("hero-test");
if (variant === "Variant A") {
  // Show variant A content
}

// Get all active variants
const variants = optimo.getVariants();
```

## Consent

```js
// Grant consent (enables storage)
optimo.grantConsent(true);

// Revoke consent (clears all data)
optimo.grantConsent(false);

// Check consent status
if (optimo.hasConsent) { ... }
```

## More methods you can call

| Method | Description |
| --- | --- |
| `optimo.converted("test-key")` | Record a conversion on a test's main goal from your own code — for a custom in-app action that isn't a plain click or page visit. Counts once per visitor. |
| `optimo.uuid()` | Returns a promise with the visitor's anonymous ID once the SDK is ready. Pass it to your server so a conversion you record from the backend is tied to the right visitor. |
| `optimo.reload()` | Ask Otter to fetch the visitor's active tests again. The SDK already does this on in-app navigation, so you only need it for manual control in a single-page app. |
| `optimo.execute("test-id")` | Re-apply a specific test's changes to the page. Useful if your single-page app re-rendered and wiped a variant's changes. |

## Frequently asked questions

### Where is the global optimo object available?

On window.optimo, once the SDK has loaded. The script is async, so the object may not exist immediately when your page-level code runs. The install snippet sets up a queue at window.optimoq for that case — push calls onto it as arrays (window.optimoq.push(['track', '...'])) and the SDK replays them once it initializes.

### Do I need to call any init function?

No. The SDK initializes automatically when the script loads. It reads the key attribute on the <script> tag, fetches your test configuration, assigns variants, applies DOM changes, fires the anti-flicker reveal, and starts listening for events — all without manual setup.

### What's the difference between optimo.track and optimo.revenue?

optimo.revenue is a convenience wrapper around optimo.track. It fires an event named 'Purchase' with the value and currency you pass. If you want to use a different event name (say, 'Subscription'), call optimo.track('Subscription', { value: 49, currency: 'USD' }) directly — you get the same behavior with full control over the event name.

### When should I call optimo.identify vs optimo.userID?

Call optimo.userID('stable_id') the moment you know who the visitor is — typically right after login. This makes variant assignment user-scoped, so the same user gets the same variant across devices. Call optimo.identify({ ...traits }) to attach profile traits (email, plan, signup date) that you can later use as targeting fields. Order matters: userID first, then identify.

### What does optimo.getVariant return for tests the visitor isn't in?

It returns null. This lets you write code that gracefully no-ops when a test hasn't been assigned, hasn't started, or isn't targeted at the current visitor. If you call optimo.getVariants() (plural), you get an object keyed by test slug for every test the visitor has been assigned to.

### Is the SDK CSP-friendly?

The script needs script-src https://www.otterab.com and connect-src https://www.otterab.com. The anti-flicker style tag needs style-src 'unsafe-inline' or a nonce-friendly install (in progress). The SDK does not inject inline scripts, eval, or remote-loaded code at runtime.

### Does optimo.grantConsent affect existing data?

optimo.grantConsent(false) immediately stops tracking, clears the visitor's ID cookie and any cached or queued data in the browser, and tells Otter to delete that visitor's record on our side — including their sessions, variant assignments, conversions, and events. It's a clean removal, which makes it a handy way to honour a "forget me" request. optimo.grantConsent(true) turns tracking back on, but the visitor starts fresh with a brand-new ID; their previous assignments aren't restored.

### How do I record a conversion from my own code?

Two options. For your test's main goal, call optimo.converted('your-test-key') — it records one conversion for the visitor on that goal (and won't double-count). For anything else, call optimo.track('event_name', { ... }) with your own event name and properties, and point a goal at that event. Use these when a conversion isn't a plain click or page visit — for example, a custom in-app action.

### My single-page app changes pages without a full reload — what should I call?

The SDK already watches for in-app navigation and re-checks things on its own, so most apps need nothing extra. If you do need manual control: optimo.reload() asks Otter to fetch the visitor's active tests again (handy when you've moved to a new view), and optimo.execute('test-id') re-applies a specific test's changes to the page if your app re-rendered and wiped them.

### How do I get the visitor's ID for server-side tracking?

Call optimo.uuid(). It returns a promise that resolves to the visitor's anonymous ID once the SDK is ready. Pass that ID to your own server so a conversion you record from the backend is tied to the same visitor the browser saw. (See the REST API section for the server-side conversion call.)

---

Canonical page: https://www.otterab.com/docs/developer-reference/sdk-api
