# JavaScript core and API reference

URL: https://traqio.app/docs/sdks/javascript
Last updated: 2026-10-22

@traqio/core is the engine behind the React Native, Expo and Capacitor SDKs: its init options, methods, queue, storage and the native bridge, documented once.

`@traqio/core` is Traqio's platform-neutral JavaScript engine: the persisted offline queue, the batching and retries, the SKAdNetwork conversion-value driver, the Apple Ads token report and deep-link resolution. It has no React Native, Expo or browser dependency; everything platform-specific arrives through a **native bridge**. [@traqio/react-native](https://traqio.app/docs/sdks/react-native) (and its alias [@traqio/expo](https://traqio.app/docs/sdks/expo)) and [@traqio/capacitor](https://traqio.app/docs/sdks/capacitor) re-export it with their bridge, so this page is the API reference of all three.

## Install

```bash
npm install @traqio/core
```

Only for a direct integration. On React Native, Expo or Capacitor, install the platform package instead — it depends on the core at an exact version.

## API reference

The SDK is a singleton, `Traqio`. Calls made while `init` is still resolving are queued; calls made before `init` is called at all are ignored — except `setConsent`, which is kept and written once storage is available. Initialise first.

### init(options)

```ts
import { Traqio } from "@traqio/core";

await Traqio.init({
  sdkKey: "YOUR_SDK_KEY",
  endpoint: "https://api.traqio.app", // origin only — the SDK appends the paths
  userId: currentUserId,              // optional, settable later with setUserId
  storage,                            // { getItem, setItem } — see "Storage"
  native,                             // a NativeBridge; omit and there is no attribution
});
```

InitOptions:
```ts
interface InitOptions {
  sdkKey: string;
  endpoint: string;
  storage?: { getItem(key: string): Promise<string | null>; setItem(key: string, value: string): Promise<void> };
  native?: NativeBridge;
  userId?: string;
  appVersion?: string;
  collectContext?: boolean; // default true — model, OS, locale; never an advertising id
  testAds?: boolean;        // AdMob test ad units: logged, never counted as revenue
  skan?: boolean;           // iOS: drive SKAdNetwork conversion values (default true)
  appleAds?: boolean;       // iOS: report the AdServices token (default true)
  deepLinking?: boolean;    // default true
  consent?: boolean;        // the user's answer to YOUR consent prompt, when known at launch
  collectAdvertisingId?: boolean; // default false — send the IDFA / GAID with the install (never prompts)
}
```

Idempotent: a second call does nothing. On first launch it reports the install once — on Android with the Play Install Referrer, waiting up to three launches if Google Play is momentarily unavailable — then resolves the deferred deep link, records the app open, fetches the SKAdNetwork mapping and reports the Apple Ads token on iOS. `endpoint` is the origin only: `https://api.traqio.app`.

### track(name, props?)

```ts
Traqio.track("signup");
Traqio.track("purchase", {
  value: 9.99,
  currency: "EUR",
  transactionId: "2000000812345678", // the store's id for this purchase
  store: "app_store",                // defaults to the platform's store
});
```

| Prop | Type | Meaning |
| --- | --- | --- |
| value | number | Revenue declared by the app — the SDK does not verify it against a receipt. |
| currency | string | ISO 4217 code. |
| transactionId | string | The store's id for the purchase (StoreKit transactionId, Play orderId). Keys the event like RevenueCat does, so one purchase reported by both is counted once. |
| store | string | app_store · play_store · mac_app_store · amazon · stripe. Defaults to the platform's store. |

On iOS every valued event also moves the SKAdNetwork conversion value. Names that Meta and TikTok can optimise on are listed in [Event names](https://traqio.app/docs/concepts/events-and-revenue#names).

### trackAdRevenue(ad)

```ts
// In AdMob's paid-event callback, once per impression:
Traqio.trackAdRevenue({
  valueMicros: 21500,         // the AdValue in micros — 0.0215 of the currency; never pre-round it
  currency: "USD",            // ISO 4217, as AdMob reports it
  precision: "estimated",     // AdMob's precisionType
  adUnitId: "ca-app-pub-0000000000000000/0000000000",
  adFormat: "rewarded",
  adSourceName: "AdMob Network", // the mediation source that filled it, when known
});
```

Impressions have their own queue (up to 1,000, sent in batches of 100) and a batch id persisted with it, so a crash between sending and the answer cannot count the same revenue twice. With `testAds: true` they are logged by the server and never counted. See [AdMob](https://traqio.app/docs/integrations/admob).

### setUserId(id)

Attaches your own user id to everything sent from then on — use the same value you give RevenueCat as `app_user_id`. Call it after sign-in. See [Identity](https://traqio.app/docs/concepts/identity).

### onDeepLink, handleUrl, getDeferredDeepLink

```ts
const unsubscribe = Traqio.onDeepLink(({ deepLink, code, deferred, via }) => {
  if (deepLink) router.push(deepLink);
});

await Traqio.handleUrl(url);           // hosts other than React Native: a URL opened the app
await Traqio.getDeferredDeepLink();    // this install's deferred link, or null
await Traqio.getInstallReferrer();     // Android: the raw Play Install Referrer
```

`onDeepLink` returns its unsubscribe function. Each link has `deepLink` (the destination set on the link, or null), `code` (the link's slug), `deferred` and `via` (`referrer`, `ip_match` or `link`). React Native wires `Linking` itself; other hosts call `handleUrl`.

### setConsent, forgetMe

```ts
await Traqio.setConsent(userAgreed);   // sharing with ad networks — not measurement

const ok = await Traqio.forgetMe();    // erase this device and user on the server, then go silent
if (!ok) showError();                  // nothing changed on either side — let the user retry
```

`setConsent` is read at send time, so events queued before a “no” leave under the “no”. `forgetMe` is irreversible on both sides: the server deletes the device's and the user's data, and the SDK goes silent for good on this install. See [Privacy](https://traqio.app/docs/concepts/privacy).

### flush, appOpened

```ts
Traqio.flush();      // send the queue now (React Native does it on background)
Traqio.appOpened();  // the app came back to the foreground (React Native does it for you)
```

An app open is reported at `init` and on each return to the foreground after at least 30 minutes away — the convention GA4, Adjust and AppsFlyer share. It is what retention on day *n* is computed from.

### Read-only properties

- `Traqio.forgotten` — true once `forgetMe()` has been honoured.
- `Traqio.skanMapping` — the conversion-value mapping in force on this device, once fetched (iOS).

## Storage

Storage is what makes the install fire exactly once and the queue survive a kill. The core takes any object with async `getItem` / `setItem`; on React Native it uses AsyncStorage automatically when the app has it. Without storage it warns at launch and reports the install again at every start.

## The native bridge

```ts
interface NativeBridge {
  platform: "ios" | "android";
  getInstallReferrer(): Promise<InstallReferrerResult>;   // Android; never rejects
  getContext?(): Promise<{ deviceModel?: string; osVersion?: string }>;
  updateConversionValue?(fine: number, coarse?: "low" | "medium" | "high", lockWindow?: boolean): void; // iOS
  getAttributionToken?(): Promise<string | null>;         // iOS 14.3+, Apple Ads
  getAdvertisingId?(): Promise<string | null>;            // only with collectAdvertisingId; never prompts
}
```

The platform packages implement it. Without a bridge, the core sends events but has no referrer and no SKAdNetwork — events, not attribution.

## Advertising id (opt-in)

Off by default: the SDK sends no IDFA or GAID. Turn on `collectAdvertisingId` only if you buy from networks that attribute by the advertising id — Unity Ads and AppLovin match their click to your install on it. It is sent with the install and never used as its identity.

```ts
Traqio.init({
  sdkKey: "YOUR_SDK_KEY",
  endpoint: "https://api.traqio.app",
  native: getNativeBridge(),
  collectAdvertisingId: true, // off by default
});
```

- iOS: the SDK never shows the App Tracking Transparency prompt — ask for it yourself before`init`, and link AdSupport. The IDFA is read only if the user already allowed tracking.
- Android: add `com.google.android.gms:play-services-ads-identifier` and the`com.google.android.gms.permission.AD_ID` permission to your app; the SDK declares neither.
- Declare the advertising id in your App Store privacy details and Google Play Data safety form.

## What it sends

| Request | When |
| --- | --- |
| POST /ingest | The install (once), then event batches of up to 20. Queue capped at 500 events. |
| POST /ingest/session | An app open — not queued: a failed one is retried by the next open. |
| POST /ingest/ad-revenue | AdMob impression batches of up to 100. |
| POST /ingest/adservices | iOS: the Apple Ads attribution token. |
| GET /skan/mapping/<sdkKey> | iOS: the conversion-value mapping. |
| POST /deeplink/deferred · /deeplink/open | Deep-link resolution. |
| POST /privacy/forget | forgetMe(). |

Every request carries the SDK key, the first-party device id and — once set — your user id and the consent answer. No advertising id, unless you set `collectAdvertisingId`: the install then carries it. These are the SDK's own routes, not a public server-to-server contract; for reading data from your servers, use the [public API](https://traqio.app/docs/api).

## Frequently asked questions

**Can I use @traqio/core on its own?**
Yes, but what you get alone is event ingestion, not attribution: attribution needs the platform evidence — the Google Play Install Referrer, SKAdNetwork — that only a native bridge can read. Apps on React Native, Expo or Capacitor should install their platform package, which includes the core and its bridge.

**Are tracking calls blocking or can they throw?**
Neither. track() and trackAdRevenue() only append to an in-memory queue persisted to storage; sending happens in the background, in batches, with exponential backoff up to five minutes. Network and HTTP errors are swallowed by design, which is also why a mistyped endpoint fails silently.
