CLItrail Docs

Collection API

The public endpoints the SDK and the hook use. You rarely call them yourself, but they are stable, small and documented here in full.

#Conventions

  • Request bodies are JSON objects of at most 16 KB, sent with Content-Type: application/json.
  • Errors answer {"error":"code"} with an HTTP status. See Error codes.
  • Browsers may call the /v1/ endpoints only from a website's registered origins: its primary origin, additional domains and registered pages. Credentials are never sent (credentials: 'omit').
  • Requests are rate limited. See Limits.
  • When a Free organisation has used its monthly quota, visits and installs still get their normal answer, with "recorded": false, and nothing is stored.
  • Every request is recorded in Logs with its exact body, rejected ones included; receipts and tokens appear there only as keyed hashes.
EndpointCalled byPurpose
GET /browser.jsWebsiteThe SDK.
GET /v1/projects/{website}/browser-configSDKGA4 measurement IDs and providers.
POST /v1/visitsSDKRecords a visit and issues a receipt.
POST /v1/visits/activateSDKConfirms the marker was written.
POST /v1/handoffsSDKIssues a handoff token.
GET /v1/projects/{website}/paths/{path}/report.shInstallerAn install path’s hook, with the path ID written in. A path the website’s organisation does not have answers 404 install_path_not_found.
GET /v1/projects/{website}/report.shInstallerThe website’s hook without a path ID; its installs count toward the first install path.
POST /v1/eventsHookReports an install.
GET /report.shAnyoneThe hook's source as plain text, for review.
GET /api/limitsAnyoneEvery plan’s limits, the prices and the service limits as JSON, cacheable for 5 minutes. See Limits.
GET /skills/clitrail-setup/…Coding agentsThe setup skill: SKILL.md, its references, manifest.json and install.sh.
GET /healthMonitoringReturns {"ok":true,"ga_mode","delivery",…}: delivery is live once any destination sends live.

#GET /v1/projects/{website}/browser-config

{
  "measurementIds": ["G-XXXXXXXXXX"],
  "providers": ["ga4", "google_ads", "meta", "tiktok", "x"]
}

The measurement IDs of the website's GA4 destinations, and its providers setting.

#POST /v1/visits

Must come from a registered origin; the page URL must be on that origin.

FieldTypeDescription
projectstringWebsite ID.
url_idstringurl_ plus the first 16 hex characters of SHA-256(origin + path), or a registered page ID.
page_urlstringThe page's URL. Stored as origin and path.
referrerstringdocument.referrer. Stored as origin and path.
identitiesobject{ ga4: { "G-…": { client_id, session_id } }, google_ads: { gclid, wbraid, gbraid, gcl_aw, session_attributes }, meta: { fbp, fbc, fbclid }, tiktok: { ttp, ttclid, ttclid_at }, x: { twclid, twclid_at }, custom: { … } }. Google Ads, Meta, TikTok and X fields for providers the website does not use are dropped; GA4 IDs are kept whenever sent.
previous_receiptstring or nullThe receipt this page's marker held before. It is revoked when the new one is activated.
sourceobject{ referrer, utm: { source, medium, campaign, term, content, id }, landed_at }
gpcbooleantrue when the browser sends Global Privacy Control.
clientobjectCoarse hints: the user-agent client hints, architecture, bitness, brave, touch, timezone, language. See Browsers and systems.
{
  "marker": {
    "format": "opfs-install-attribution",
    "version": 1,
    "project": "p_04de14934e8005ed97dd",
    "url_id": "url_96f86d390b35cfa7",
    "origin": "https://acme.dev",
    "receipt": "5GodFohsqck6lAXWFydmiZTBXvtQnU2WzbpsB8tq-_k",
    "created_at": 1790171920120,
    "expires_at": 1792763920120
  }
}

#POST /v1/visits/activate

Body { "project", "receipt" }, from the same origin as the visit. Answers {"ok":true}. Until activated, a receipt never matches.

#POST /v1/handoffs

Either { "project", "receipt" } for a visit that already has a receipt, or the fields of /v1/visits (without previous_receipt) to record a visit that exists only for the handoff. Both accept gpc. For a handoff-only visit the answer also carries its receipt, which the SDK keeps in memory to link further installation scopes of the same page view; such a receipt can never be activated or matched as a browser receipt.

{
  "ok": true,
  "token": "hV0c3t8G2mIqzW1b6Xy9kQ4rLpE7uN5sJ0aTfDgC2vY",
  "expires_at": 1792763920120
}

#POST /v1/events

Called by the hook, never by a browser: a request with an Origin header is refused with native_endpoint.

FieldTypeDescription
projectstringWebsite ID.
event_idstring8 to 128 letters, digits, _ or -. Idempotency key.
installation_idstring8 to 128 letters, digits, _ or -.
event_typestringinstall_started, install_completed or first_run.
receiptsstring[]Up to 64 receipts of 43 characters.
handoffstringOptional handoff token. receipts must then be empty.
platformstringOptional: darwin or linux.
install_pathstringOptional: the install path ID. Missing or unknown: the organisation’s first path.
arch, browsers, default_browser, timezone, languagestring, string[]Optional facts about the machine; see What it sends.
{
  "ok": true,
  "event_id": "6f2da9610246dec311f2b793f7c32902",
  "duplicate": false,
  "resolution": "matched",
  "candidate_count": 1,
  "selection_policy": "latest_visit",
  "ga": {
    "status": "pending",
    "deliveries": [{
      "destination": "d_2f7c7e7eac2f8f79c5e2",
      "status": "pending",
      "session_mode": "original_session_window"
    }]
  }
}

On Free, and for a paused install path, resolution is counted: receipts and tokens are not read. When Free has used its monthly quota, the answer is the same shape with "recorded": false, and nothing is stored. The response never contains identifiers or referring pages. ga is kept for older clients: it lists the delivery queued for each destination, with the first one's status as status (not_queued when there is none).

#Dashboard API

The dashboard uses JSON endpoints under /api/, authenticated by the session cookie and accepted only from the service's own origin. They serve the dashboard and may change; build integrations on webhooks and the endpoints above.