Skip to content

feat: add appToken config option for Cloud organic-install attribution#1

Merged
onamfc merged 4 commits into
mainfrom
feat/app-token
May 4, 2026
Merged

feat: add appToken config option for Cloud organic-install attribution#1
onamfc merged 4 commits into
mainfrom
feat/app-token

Conversation

@onamfc
Copy link
Copy Markdown
Member

@onamfc onamfc commented May 4, 2026

Companion to LinkForty/core@1.15.0

What this adds

A new optional appToken field on the SDK's config. When set, the SDK includes it in every /api/sdk/v1/install request body, letting LinkForty Cloud scope organic installs (those without a deeplink-click match - e.g. App Store discovery, social mentions, organic press) to the right workspace.

Without this, organic installs are invisible in workspace analytics. With it, they appear correctly attributed to the workspace.

How it works

  • Customers grab the token from Dashboard → Workspace Settings → App Token
  • Token format: at_<32 hex chars> - public, Stripe-style. Safe to ship in app bundles. Cannot authenticate API actions; only identifies the workspace.
  • SDK sends it on /api/sdk/v1/install alongside the existing fingerprint data
  • Self-hosted Core deployments accept the field but ignore it - no behavior change there

Backwards compatibility

  • No-op when not set. The SDK omits the field entirely from the request body when appToken is not configured. The server (any version) sees the same wire format it always has.
  • Legacy SDK adoption window: customer apps in production today continue to work without changes. They just lose visibility into organic installs in their workspace analytics until they ship an app update with this SDK version + token configured.

Test plan

  • Configure appToken on a fresh app install, verify /api/sdk/v1/install POST body includes the token
  • Omit appToken from config, verify the request body matches the pre-change shape (no appToken key)

onamfc added 4 commits May 4, 2026 14:50
Optional `appToken` field added to LinkFortyConfig. When set, the SDK
includes it in every `/api/sdk/v1/install` request body, letting Cloud
scope organic installs (those with no click match) to the right
workspace.

Threaded through reportInstall() and collectFingerprint() so the
fingerprint payload carries the token only when configured. Custom
encode(to:) on DeviceFingerprint uses encodeIfPresent for both deviceId
and appToken so legacy/self-hosted servers see the same wire format
they always have.

Backwards-compatible: when not set, the SDK omits the field and the
server falls back to the existing organic-installs-go-unscoped behavior.

Token format: `at_<32 hex chars>` — public, designed to ship in app
bundles. Find it in the LinkForty dashboard under Workspace Settings →
App Token. Self-hosted single-tenant deployments don't need it.

Companion to LinkForty/cloud#76 + LinkForty/core@1.15.0.
The original feat commit added appToken to FingerprintCollector but
not to its protocol or the test mock — SPM build failed with:
- 'FingerprintCollector does not conform to FingerprintCollectorProtocol'
- 'extra argument appToken in call'

Also addresses a SwiftLint --strict failure (LinkForty.swift:165) that
the fresh runner's newer SwiftLint surfaced as an error: implicit_return
violation on getExternalUserId().

Changes:
- Add appToken: String? to FingerprintCollectorProtocol.collectFingerprint
- Mirror the new parameter on MockFingerprintCollector and capture it in
  lastAppToken so tests can assert it's plumbed through (default nil
  preserves existing call sites)
- Pass appToken through to DeviceFingerprint() in the mock so it round-
  trips correctly
- Drop redundant 'return' from getExternalUserId() to satisfy SwiftLint
  implicit_return
- Add CHANGELOG.md [Unreleased] entry covering the appToken feature
  and explaining the public-token model + Cloud-vs-Core behavior
Swift protocol method requirements can't carry default parameter values,
so the existing call to fingerprintCollector.collectFingerprint() in
DeepLinkHandler (which calls through the FingerprintCollectorProtocol
abstraction) had to be updated to pass appToken explicitly.

The concrete FingerprintCollector and MockFingerprintCollector still
have appToken: String? = nil defaults, which is why FingerprintCollector
test call sites (typed as the concrete class) compile without changes.
Only protocol-typed call sites need the explicit nil — and there's only
one: this DeepLinkHandler URL-resolution path. The deep link resolution
flow doesn't need a token because resolution is workspace-agnostic on
the server side; nil is correct here.
@onamfc onamfc merged commit 398f1fc into main May 4, 2026
5 checks passed
@onamfc onamfc deleted the feat/app-token branch May 4, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant