@clakta/javascript

中文文档
1.1.4发布于 2026/11/01MITTypeScriptESM + CJS零依赖

@clakta/javascript

原始使用文档
文档目录25 节
  1. Installation
  2. Usage
  3. With a script tag
  4. Options
  5. How it works
  6. Tracking modes
  7. Consent
  8. What happens when the visitor answers
  9. Personal data in URLs
  10. Automatic tracking
  11. API
  12. pageView()
  13. trackEvent(trackingId, eventType, metadata?)
  14. identify(customer)
  15. Ecommerce
  16. decorateLemonSqueezyUrl(url)
  17. decoratePaddleCheckout(options)
  18. decorateCaktoUrl(url)
  19. decorateKiwifyUrl(url)
  20. decorateHotmartUrl(url)
  21. consent.update(enabled) / getConsent()
  22. TypeScript
  23. Server-side rendering
  24. Links
  25. Tracking transport regression test

@clakta/javascript

The official Clakta SDK for the browser.

Clakta is a modern sales and ads tracking platform built for modern businesses. Get accurate data and know which ads perform best, which influencer sells the most, with a modern and powerful platform.

Website

npm version npm downloads minzipped size license


Framework-agnostic tracking for any website or web app. Zero dependencies, works with a bundler or a single <script> tag.

Installation

npm install @clakta/javascript
pnpm add @clakta/javascript
yarn add @clakta/javascript

Or load it straight from the CDN — no build step required:

<script src="https://cdn.clakta.com/script.js" defer></script>

Pin a specific version if you'd rather control upgrades yourself:

<script src="https://cdn.clakta.com/v1.0.8/script.js" defer></script>

Usage

Nothing is collected until you call init, so call it as early as possible — ideally before your app renders — and pass your script id. Everything else is optional.

import clakta from "@clakta/javascript"

clakta.init({ id: "YOUR_SCRIPT_ID" })

Named imports work too, if you prefer them or want the smaller bundle:

import { init, trackEvent } from "@clakta/javascript"

init({ id: "YOUR_SCRIPT_ID" })
trackEvent("hero-cta", "click")

With a script tag

The CDN build exposes a Clakta global:

<script src="https://cdn.clakta.com/script.js" defer></script>
<script>
  window.addEventListener("load", function () {
    Clakta.init({ id: "YOUR_SCRIPT_ID" })
  })
</script>

To skip the bootstrap entirely, add data-id to the tag and the SDK initializes itself on load:

<script
  src="https://cdn.clakta.com/script.js"
  data-id="YOUR_SCRIPT_ID"
  defer
></script>

If you keep data-id on the tag but want to call init yourself, with options the tag can't express, add data-auto-init="false". Otherwise the tag initializes first and your init call is ignored as a duplicate.

After init runs, the full API is also available on window.clakta — handy for inline scripts, Google Tag Manager, or any code that can't import modules:

<button onclick="window.clakta.trackEvent('hero-cta', 'click')">Buy now</button>

Options

OptionTypeDefaultDescription
idstring—Required. Your Clakta script id.
apiHoststringhttps://api.clakta.comOverride the API host. Tracking/config use https://t.clakta.com by default; Stripe stays on api.clakta.com.
debugbooleanfalseVerbose console logging.
consentbooleanby visitor locationWhether the visitor has consented to first-party identifiers. See Consent.

How it works

On init the SDK asks the Clakta API how this source should collect data — the tracking mode plus which events are tracked automatically. Those are dashboard settings, so they're resolved at runtime rather than baked into your build.

Two consequences worth knowing:

  • Nothing is sent until that config resolves. Calls you make in the meantime (an identify right after init, say) are buffered and replayed once it does.
  • If the config can't be resolved — an unknown script id, or an unreachable API — the SDK stays inert and sends nothing at all, rather than guessing how the visitor may be tracked.

Tracking modes

Every project collects data in one of two modes, set in your Clakta dashboard:

ModeBehavior
privacy_friendlyNo cookies or first-party identifiers, and no browser storage is touched. The server derives the session. identify is a no-op.
full_attributionStorage-backed sessions, a persistent visitor id and richer client context, subject to the visitor's consent.

The mode isn't a client option — it comes from the project settings so it stays consistent across every surface you install the SDK on.

Under full_attribution with consent, the SDK keeps a single first-party cookie, _clakta, on your registrable domain (with a localStorage copy). It holds the visitor id, which expires a year after it was created — later visits don't extend it — the current session, which ends after 30 minutes of inactivity, and the click token (clkt) the session landed with. The visitor id is what keeps one person counted once when their IP changes mid-visit — routine on mobile networks — or when they move between your subdomains. A second cookie, _clakta_consent, remembers the visitor's answer to your cookie banner; see Consent.

Earlier versions of the SDK kept these values in separate cookies (clakta_session_id, clakta_visitor_id, clakta_has_visited and clakta_landing_page). The first time a consenting visitor returns, their ids are carried over into _clakta and the old cookies are deleted, so nobody is counted as a new visitor.

Consent only has an effect under full_attribution; in privacy_friendly mode no identifiers are ever used regardless.

Unless you set it, consent follows where the visitor is. Visitors in the EU, the EEA, the UK and Switzerland — and any visitor whose location is unknown — start without consent, as the GDPR and the ePrivacy rules require, and are only identified once your cookie banner calls updateConsent(true). Everyone else starts with it and can still decline. Clakta works out the location from the request when the SDK loads its configuration; nothing about it is stored on the device.

A visitor without consent is still counted, anonymously: nothing is stored on their device, no session or visitor id is sent, and Clakta records the visit the privacy_friendly way — a server-derived session and a visitor id that changes every day. Everything else is still recorded: page views with their UTM tags, clkt and ad click ids (gclid, fbclid, …), clicks, scrolls and form submissions, and the shopping funnel — pageViewWithProduct, addToCart, initiateCheckout and purchase — with only the customer details left out.

Set the starting value yourself at init — false to ask every visitor wherever they are, or true if the SDK only loads once the visitor has accepted:

clakta.init({ id: "YOUR_SCRIPT_ID", consent: false })

Or, on the script tag, with data-consent:

<script
  src="https://cdn.clakta.com/script.js"
  data-id="YOUR_SCRIPT_ID"
  data-consent="false"
  defer
></script>

And update it when the visitor makes a choice in your cookie banner:

import { getConsent, updateConsent } from "@clakta/javascript"

updateConsent(true) // or clakta.consent.update(true)
getConsent() // => true

getConsent() reports false until the choice is made — by you, by the visitor, or by their location once the configuration has loaded.

What happens when the visitor answers

The answer is remembered in a _clakta_consent cookie on your registrable domain, shared by every subdomain and kept for a year from the latest answer, and in a localStorage copy with no expiry, which keeps it on the site it was given on after the cookie is gone. Later pages apply it from their first event, so your banner only has to report the answer once, when the visitor gives it; reporting it again on every page is harmless and renews the cookie. A refusal is remembered too — that is what honouring it on the next page takes — and it is the one thing the SDK stores without consent. Nothing is stored for privacy_friendly sources.

Your own consent value outranks a remembered answer. true says the visitor accepted. false keeps every visitor anonymous, whatever they answered before, until they accept on the current page — so with false, your banner has to call updateConsent(true) (or set window.claktaConsent) on every page for visitors who accepted.

When a visitor accepts mid-visit, the SDK starts their identified session right away and records the current page in it. The session takes its click token (clkt) from the URL this page load started on — the one the visitor arrived with from your ad — so a sale that follows keeps its ad source. That page view also names the anonymous view of the same page, and Clakta drops the anonymous one so the page is counted once: page views from visitors who haven't answered the banner yet are held for 60 seconds before they reach your dashboards, which covers most banner answers. Page views from visitors who refused aren't held. A visitor who answers later has that page counted in both sessions. Pages viewed before the page they accepted on stay in the anonymous part of the visit, since nothing could be stored for them without consent.

A banner can answer before a deferred script tag has loaded, when clakta.consent doesn't exist yet. Set window.claktaConsent instead, and init picks it up over everything else — the starting value, a remembered answer and the visitor's location:

window.claktaConsent = true

Revoking consent immediately clears every identifier the SDK stored, and subsequent events are recorded anonymously as above.

Personal data in URLs

Every event carries the page URL, so values in it that name a person or work as a credential are replaced with redacted before they leave the browser, whatever the visitor's consent: any value that contains an email address, and parameters such as email, phone, token, password or first_name. The same applies to the referrer, outbound links and form actions. URLs with nothing to replace are sent exactly as they are.

Automatic tracking

Once initialized, the SDK installs only the listeners your project has enabled in the dashboard:

  • Page views — tracked on load and on every SPA navigation (pushState, replaceState, popstate, hashchange). Repeat views of the same URL are debounced.
  • Time on page — counted only while the page is visible, and reported when the visitor hides the tab, closes the page or navigates within your app, so the last page of a visit is timed too. Comes with page views; it has no switch of its own.
  • Clicks — outbound links, plus any element you explicitly opt in with data-clkt-track-click. Clicks elsewhere are ignored, so you don't get an event for every click on the page.
  • Scroll depth — 25% / 50% / 75% / 100% milestones, with the furthest depth reached emitted on exit for short pages.
  • Form submissions — form id, action, and field count. Field values are never collected, in either tracking mode.
<button data-clkt-track-click id="hero-cta">Buy now</button>

Everything below can be called manually whether or not automatic tracking is on.

API

pageView()

Records a page view for the current URL. Useful when automatic page views are off, or for virtual page views your framework doesn't route through the History API.

clakta.pageView()

trackEvent(trackingId, eventType, metadata?)

Tracks a custom event in the current session.

clakta.trackEvent("hero-cta", "click", { variant: "A" })
clakta.trackEvent("onboarding", "signup", { plan: "pro" })

identify(customer)

Attaches customer details to the session — call it when you learn who the visitor is, e.g. on login or signup. At least one of externalId, claktaCustomerId, or email is required; any extra fields are forwarded as free-form traits.

clakta.identify({
  externalId: "user_123",
  email: "[email protected]",
  name: "Ada Lovelace",
  plan: "pro", // extra fields become traits
})

Identifying a visitor is the opposite of anonymous collection, so this is a no-op under privacy_friendly and when consent has been withheld.

Ecommerce

Three funnel events cover the path to a sale. Each keeps anything extra you pass as metadata, so you can hand it the shape your platform already uses.

// Add to cart — no order exists yet
clakta.addToCart({
  products: [{ id: "42", name: "Tee", price: 29.99, quantity: 2 }],
  totalPrice: 59.98,
  currency: "USD",
})

// Checkout started
clakta.initiateCheckout({
  orderId: "1001",
  totalPrice: 59.98,
  currency: "USD",
  products: [{ id: "42", name: "Tee", price: 29.99, quantity: 2 }],
  customer: { email: "[email protected]" },
})

// Purchase completed
clakta.purchase({
  orderId: "1001",
  totalPrice: 59.98,
  currency: "USD",
  paymentMethod: "credit_card",
  products: [{ id: "42", name: "Tee", price: 29.99, quantity: 2 }],
  customer: { email: "[email protected]" },
  // Optional — clkt is read from the URL when you don't pass one
  tracking: { utm_source: "facebook", utm_medium: "cpc" },
})

There's also pageViewWithProduct({ id, name, price, currency }) for product detail page views.

The order is identified, not defined

initiateCheckout and purchase require orderId — the order's id in your own database, and the same value your server sends to the Orders API. Calls without one are dropped with a console warning.

That id is the only thing the browser event really has to get right. Clakta reconciles it against the order your server posts, and the server's order stays the source of truth for status, totals and contents. Two consequences worth designing around:

  • A visitor who closes the tab before your thank-you page renders costs you nothing — the server-side order still lands.
  • The client can't invent or amend an order. Fields the server owns (projectId, platform, status, timestamps) aren't accepted here at all.

The rest is a small identifying subset of the Orders API payload, using the same field names so both sides describe the order the same way:

FieldTypeNotes
orderIdstring | numberRequired. Numbers are normalized to strings.
totalPricenumberDecimal, e.g. 59.98.
currencystringISO 4217. Defaults to the project currency.
paymentMethodClaktaPaymentMethodcredit_card, pix, paypal, apple_pay, … (the Orders API enum).
products{ id, name, quantity, price }[]Product ids are normalized to strings.
customer{ email, phone, name }Identifiers only. See below.
trackingClaktaTrackingParamsAttribution parameters. See below.

customer is personal data, so it's sent only under full_attribution with consent granted — in privacy_friendly mode the block is dropped from the payload entirely, the same way identify is a no-op there. The event itself is still recorded: orderId alone is enough to reconcile it.

addToCart takes the same products / totalPrice / currency fields but no orderId, since the order doesn't exist yet.

Attribution

initiateCheckout and purchase accept an optional tracking block — the same one your server sends to the Orders API, so both sides credit the order to the same click:

FieldTypeNotes
clktstringThe Clakta click token. Resolved from the URL when omitted — see below.
utm_sourcestringe.g. "facebook".
utm_mediumstringe.g. "cpc".
utm_campaignstringe.g. "summer-sale".
utm_contentstringe.g. "video-a".
clakta.purchase({
  orderId: "1001",
  totalPrice: 59.98,
  tracking: {
    clkt: "meta:facebook:123456:987654:555555", // optional
    utm_source: "facebook",
    utm_medium: "cpc",
    utm_campaign: "summer-sale",
    utm_content: "video-a",
  },
})

Every field is optional — an order sent without any of them is recorded as unattributed — and anything extra you pass is kept as metadata.

clkt is the one value the SDK can recover on its own, because it usually isn't there to pass: the token arrives on the ad's landing URL, and by the time the visitor reaches checkout the query string is long gone. So when you omit it, the SDK looks for ?clkt= on the current URL, then on the URL the visit landed with. That fallback reads stored state, so it applies under full_attribution with consent granted; in privacy_friendly mode no storage is touched and only the current URL is checked. Pass clkt yourself — from your own server-side record of the order, say — and your value is used as-is.

decorateLemonSqueezyUrl(url)

Lemon Squeezy checkout links can carry the visitor's session, and the SDK adds it for you: when someone clicks a link to *.lemonsqueezy.com/checkout/buy/… or /buy/…, it appends checkout[custom][clakta_session_id] (and checkout[custom][clkt] when the visit carries a click token) before the click goes through. Lemon Squeezy stores that permanently against the order and sends it back on the webhook, so Clakta credits the sale to the visitor who made it. Hosted checkouts and Lemon.js overlay buttons are both covered, and nothing is added to a single-use ?cart= URL or to a key you already set yourself.

You only need this function for a checkout you open yourself:

LemonSqueezy.Url.Open(
  clakta.decorateLemonSqueezyUrl("https://acme.lemonsqueezy.com/buy/abc-123"),
)

Building checkouts through Lemon Squeezy's API instead? Pass the session in checkout_data.custom when you create them — it's the same key, and it's the most reliable option because it never depends on the browser:

custom: {
  clakta_session_id: claktaSessionId
}

The URL is returned untouched in privacy_friendly mode and when consent was declined: those visitors have no client-side id, and Clakta derives their identity server-side instead.

decoratePaddleCheckout(options)

Paddle checkouts carry the visitor's session in customData, and the SDK adds it for you — both ways a checkout can open:

  • HTML checkout buttons. When someone clicks an element with Paddle's paddle_button class, the SDK merges clakta_session_id (and clkt, when the visit carries a click token) into its data-custom-data before Paddle.js reads it.
  • Paddle.Checkout.open(). The SDK wraps the method once Paddle.js loads, so a programmatic overlay or inline checkout is covered with no code of your own.

This is worth more on Paddle than anywhere else: Paddle copies a transaction's custom data onto the subscription it creates, and from there onto every renewal — so one decoration at checkout attributes a subscription's whole lifetime.

You only need the function itself when you build the options object somewhere the SDK can't see it:

Paddle.Checkout.open(
  clakta.decoratePaddleCheckout({
    items: [{ priceId: "pri_01gm81eqze2vmmvhpjg13bfeqg", quantity: 1 }],
  }),
)

Creating transactions through Paddle's API instead? Pass the session in custom_data when you create them — it's the same key, and it's the most reliable option because it never depends on the browser:

custom_data: {
  clakta_session_id: claktaSessionId
}

Keys you already set yourself are never overwritten, and the options are returned untouched in privacy_friendly mode and when consent was declined: those visitors have no client-side id, and Clakta derives their identity server-side instead.

decorateCaktoUrl(url)

Cakto checkout links can carry the visitor's session, and the SDK adds it for you: when someone clicks a link to pay.cakto.com.br/…, it appends ?sck= with the session id before the click goes through. Cakto stores that against the order and reports it back on the webhook, so Clakta credits the sale to the visitor who made it.

Cakto also keeps the value in a cookie on its own domain and applies it to the purchase that follows, so decorating the link a visitor clicks is enough — they can move around the checkout and still be credited.

You only need the function itself for a URL you build somewhere the SDK can't see it, such as a server-side redirect or a router link:

window.location.href = clakta.decorateCaktoUrl(
  "https://pay.cakto.com.br/AA58c2n",
)

sck is the only field Cakto offers, so unlike Lemon Squeezy and Paddle the click token doesn't ride along — it's already recorded against the session server-side. Cakto's other parameter, src, is deliberately left alone: it never reaches the webhook, so writing to it would be invisible to Clakta while trampling a value you may be using. An sck you set yourself is never overwritten, and the URL is returned untouched in privacy_friendly mode and when consent was declined: those visitors have no client-side id, and Clakta derives their identity server-side instead.

decorateKiwifyUrl(url)

Kiwify checkout links can carry the visitor's session, and the SDK adds it for you: when someone clicks a link to pay.kiwify.com.br/… (or pay.kiwify.com/…), it appends ?sck= with the session id before the click goes through. Kiwify stores that against the order and reports it back inside TrackingParameters on the webhook, so Clakta credits the sale to the visitor who made it.

You only need the function itself for a URL you build somewhere the SDK can't see it, such as a server-side redirect or a router link:

window.location.href = clakta.decorateKiwifyUrl(
  "https://pay.kiwify.com.br/dF32ZQ8",
)

sck is the only field Kiwify offers, so as with Cakto the click token doesn't ride along — it's already recorded against the session server-side. Kiwify's other free-form slot, src, is deliberately left alone: merchants use it for their own source labels and read it back in Kiwify's own reporting. An sck you set yourself is never overwritten, and the URL is returned untouched in privacy_friendly mode and when consent was declined: those visitors have no client-side id, and Clakta derives their identity server-side instead.

Decoration is matched on Kiwify's own checkout hosts, so a checkout served from a custom domain of yours is returned untouched — append sck yourself in that case, using clakta.getSessionId().

decorateHotmartUrl(url)

Hotmart checkout links can carry the visitor's session, and the SDK adds it for you: when someone clicks a link to pay.hotmart.com/…, it appends ?sck= with the session id before the click goes through. Hotmart stores that against the purchase and reports it back on the order, so Clakta credits the sale to the visitor who made it.

Hotlinks are decorated too. go.hotmart.com/… is the domain most producers and affiliates actually share, and it forwards the tracking parameters it receives on to the checkout it lands on — so a sale that starts from a shared hotlink is attributed the same way one starting at pay. is.

You only need the function itself for a URL you build somewhere the SDK can't see it, such as a server-side redirect or a router link:

window.location.href = clakta.decorateHotmartUrl(
  "https://pay.hotmart.com/P12345678X",
)

sck is the only field Clakta writes, so as with Cakto and Kiwify the click token doesn't ride along — it's already recorded against the session server-side. Hotmart's other parameters are deliberately left alone: src is the source label you already use in Hotmart's own reporting, and xcod carries Hotmart's affiliate hotlink code, so overwriting it could cost an affiliate their commission on the sale. An sck you set yourself is never overwritten, and the URL is returned untouched in privacy_friendly mode and when consent was declined: those visitors have no client-side id, and Clakta derives their identity server-side instead.

Decoration is matched on Hotmart's own hosts, so a checkout served from a custom domain of yours is returned untouched — append sck yourself in that case, using clakta.getSessionId().

consent.update(enabled) / getConsent()

See Consent.

TypeScript

The package ships its own types — no @types/* needed.

import clakta, {
  type ClaktaCartDetails,
  type ClaktaOptions,
  type ClaktaOrderCustomer,
  type ClaktaOrderDetails,
  type ClaktaPaymentMethod,
  type ClaktaProduct,
  type ClaktaTrackingParams,
  type CustomerData,
  type ResolvedConfig,
  type TrackingMode,
} from "@clakta/javascript"

Server-side rendering

Every entry point is browser-only and no-ops safely when window is undefined, so importing the package in an SSR build won't throw. Calls made on the server simply do nothing.

Tracking transport regression test

From the repository root, run pnpm dlx [email protected] --test packages/javascript/test/ingestion.test.ts. This checks fetch/sendBeacon routing, unchanged payload session identifiers, custom hosts, and Stripe checkout isolation.

版本与发布记录

@clakta/javascript 共发布 11 个版本,首次发布于 2026/08/01;过去 12 个月发布了 11 个版本。

版本发布日期距今
1.1.4latest2026/11/012 天前
1.1.32026/10/268 天前
1.1.22026/10/1519 天前
1.1.12026/10/1222 天前
1.1.02026/10/0925 天前
1.0.82026/10/031 个月前

关于 @clakta/javascript 的常见问题

@clakta/javascript 是什么?

@clakta / javascript

如何安装 @clakta/javascript?

在项目目录运行 npm install @clakta/javascript,或使用 pnpm add / yarn add / bun add。

@clakta/javascript 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@clakta/javascript 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@clakta/javascript 可以商用吗?

@clakta/javascript 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@clakta/javascript 还在维护吗?

最新版本 1.1.4 发布于 2026/11/01(2 天前);至今共发布 11 个版本,其中过去 12 个月发布了 11 个;首次发布于 2026/08/01。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。