Purpose: Single source of truth for what every LinkForty SDK must support. When a new feature is added to the platform, update this document first, then update each SDK to match.
This spec defines what each SDK must accomplish, not how. Implementation details (method names, parameter conventions, error types) are platform-specific and left to each SDK.
Last updated: 2026-03-12
- 1. Initialization
- 2. Deferred Deep Linking (Install Attribution)
- 3. Direct Deep Linking
- 4. Server-Side URL Resolution
- 5. Event Tracking
- 6. Revenue Tracking
- 7. Programmatic Link Creation
- 8. User Identity
- 9. Attribution Data Access
- 10. Data Management
- 11. Configuration
- 12. Error Handling
- 13. Offline Resilience
- API Endpoints Reference
- Models Reference
- Feature Parity Matrix
The SDK must be initialized before any other functionality is used. Initialization performs these steps:
- Validate configuration (base URL, attribution window bounds, HTTPS enforcement)
- Set up internal components (networking, storage, fingerprint collection, deep link handling)
- Detect first launch and persist that state
- On first launch: collect a device fingerprint and report the install to the server (see Deferred Deep Linking)
- On subsequent launches: load cached attribution data from local storage
- Initialization must be idempotent or guard against double-init
- Methods that require initialization must fail clearly if called before init
- The SDK should be usable as a singleton or single shared instance
Matches a new app install back to the link click that drove it, using probabilistic device fingerprinting.
- On first launch, collect a device fingerprint containing:
- User agent string
- Timezone identifier
- Device language
- Screen dimensions (native pixels)
- Platform (
iOS/Android) - Platform version
- App version
- Optional device ID (IDFA/GAID, only if user consented)
- Send the fingerprint to the server
- Receive an attribution response containing: install ID, whether attributed, confidence score, matched factors, and deep link data (if attributed)
- Persist the install ID and deep link data locally
- Deliver the result to a registered callback (or null/nil for organic installs)
- Allow the callback to be registered before or after initialization (if registered after and data is already available, invoke immediately)
POST /api/sdk/v1/install -- see endpoints reference
Handles the case where a user taps a LinkForty link and the app is already installed. The OS opens the app directly (via Universal Links on iOS, App Links on Android, or custom URL schemes).
- Accept an incoming URL from the app's deep link handler
- Parse the URL to extract the short code, UTM parameters, and custom query parameters
- If the SDK is configured with a server connection, resolve the URL server-side for enriched data (see Server-Side URL Resolution)
- Deliver the URL and parsed data to a registered callback
- Support multiple callback registrations
- iOS: The app must manually pass the URL to the SDK (from
onOpenURLorAppDelegate) - Android: Same -- the app passes the intent URL to the SDK
- React Native / Expo: The SDK may automatically listen via the platform's linking API, but must still support explicit URL handling
When a Universal Link or App Link bypasses the redirect server (the OS opens the app directly), the SDK must resolve the link server-side to get the full link metadata that would normally come from the redirect.
- Extract path segments from the URL to determine the resolve path:
- Single segment:
/api/sdk/v1/resolve/{shortCode} - Two segments:
/api/sdk/v1/resolve/{templateSlug}/{shortCode}
- Single segment:
- Collect a device fingerprint and send it as query parameters (
fp_tz,fp_lang,fp_sw,fp_sh,fp_platform,fp_pv) - Return the enriched
DeepLinkDatafrom the server response - Fall back to local URL parsing if the server request fails (network error, timeout, etc.)
GET /api/sdk/v1/resolve/:shortCode (or /:templateSlug/:shortCode) -- see endpoints reference
Tracks in-app events for conversion attribution. Events are tied to the install ID so the server can correlate them with the original link click.
- Accept an event name and optional properties dictionary
- Send the event to the server with the install ID and timestamp
- Handle failures gracefully (see Offline Resilience)
POST /api/sdk/v1/event -- see endpoints reference
A convenience for tracking revenue-specific events with structured amount and currency fields.
- Accept an amount, currency code, and optional additional properties
- Send as an event with revenue fields included in the event data
This can be a dedicated method or handled through the general event tracking method with structured properties. The key requirement is that revenue events include amount and currency as distinct fields in the event data sent to the server.
Allows apps to create short links on behalf of the user (e.g., for sharing content).
- Accept link creation options including an optional template ID, deep link parameters, title, description, custom code, UTM parameters, and external user ID
- Require an API key to be configured
- Choose the appropriate endpoint:
- With template ID: Send to
POST /api/links(dashboard endpoint, requires explicit template) - Without template ID: Send to
POST /api/sdk/v1/links(simplified endpoint, auto-selects the organization's most recent template)
- With template ID: Send to
- Return the created link's URL, short code, link ID, and deduplication status
- If an SDK-level external user ID is set (see User Identity), include it in the request body unless overridden per-call
POST /api/sdk/v1/links— simplified endpoint (auto-selects template, Cloud only)POST /api/links— dashboard endpoint (requires template ID)
- This feature requires an API key. SDKs must fail clearly if no API key is configured.
Allows apps to associate an external user identifier with SDK operations, primarily link creation. This enables per-user deduplication and share attribution on the dashboard.
- Provide a method to set an external user ID (any string: UUID, email, integer, etc.)
- Provide a method to get the current external user ID
- Passing null/nil clears the stored ID
- The stored ID is automatically attached to all
createLink()calls unless overridden per-call viaCreateLinkOptions.externalUserId - The ID is stored in memory only (not persisted to disk)
- Clearing SDK data or resetting the SDK also clears the external user ID
- The external user ID does not require initialization — it can be set before or after
initialize() - Per-call
externalUserIdinCreateLinkOptionstakes precedence over the SDK-level value - The value is not sent to any endpoint automatically — it is only included in link creation requests
The SDK must provide access to cached attribution data from local storage without requiring a network call.
- Install ID -- the server-assigned UUID for this install
- Install data -- the
DeepLinkDatafrom attribution (null if organic) - First launch status -- whether this is the first launch of the app
- Clear data -- wipe all locally stored SDK data (install ID, attribution data, cached deep links, event queue). Used for testing and GDPR compliance.
- Reset -- return the SDK to an uninitialized state so it can be re-initialized. This is separate from clearing data.
| Field | Description |
|---|---|
| Base URL | The LinkForty server URL (e.g., https://go.yourdomain.com) |
| Field | Description | Default |
|---|---|---|
| API key | Required for link creation and Cloud features | None |
| Debug mode | Enable verbose logging | Off |
| Attribution window | How far back to match installs to clicks | 7 days (168 hours) |
- Base URL must be HTTPS (except
localhost/127.0.0.1for local development) - Attribution window must be between 1 hour and 2160 hours (90 days)
SDKs must handle these error scenarios. The mechanism (typed enums, error codes, exceptions) is platform-specific.
| Scenario | Expected behavior |
|---|---|
| Method called before initialization | Throw/return an error |
| Double initialization | Warn or throw |
| Network failure during install report | Treat as organic install, deliver null to callback |
| Network failure during URL resolution | Fall back to local URL parsing |
| Network failure during event tracking | Queue the event for retry (see Offline Resilience) |
| Link creation without API key | Throw/return an error |
| Server returns error response | Surface the error to the caller |
| Invalid configuration | Throw/return an error during initialization |
Events should be queued locally when the network is unavailable and retried when connectivity is restored.
- Maximum queue size: 100 events
- Queue must persist across app restarts (local storage)
- Provide a way to manually flush the queue
- Provide a way to clear the queue without sending
- Provide a way to check the queue size
- Install reporting: if the network call fails on first launch, treat as organic. The install can be re-attributed on a subsequent launch if the SDK detects it hasn't successfully reported yet.
- URL resolution: fall back to local parsing (never block the deep link flow on a network call)
All endpoints are relative to the configured base URL.
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/api/sdk/v1/install |
None | Report install, get deferred deep link |
GET |
/api/sdk/v1/resolve/:shortCode |
None | Resolve link without redirect |
GET |
/api/sdk/v1/resolve/:templateSlug/:shortCode |
None | Resolve template link without redirect |
POST |
/api/sdk/v1/event |
None | Track in-app events |
GET |
/api/sdk/v1/health |
None | Health check |
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST |
/api/sdk/v1/links |
API key | Create link (auto-selects template) |
POST |
/api/links |
API key | Create link (requires template ID) |
When an API key is configured, send it as: Authorization: Bearer <api_key>
Canonical field names for cross-SDK data models. SDKs should use platform-appropriate naming conventions (camelCase for Swift/Kotlin, camelCase for JS/TS) but the data must map to these fields.
| Field | Type | Required | Description |
|---|---|---|---|
| shortCode | string | Yes | The link's short code |
| iosUrl | string | No | iOS destination URL |
| androidUrl | string | No | Android destination URL |
| webUrl | string | No | Web fallback URL |
| utmParameters | UTMParameters | No | UTM tracking parameters |
| customParameters | map<string, string> | No | Custom query parameters |
| deepLinkPath | string | No | In-app routing path (e.g., /product/123) |
| appScheme | string | No | App URI scheme (e.g., myapp) |
| clickedAt | datetime | No | When the link was clicked (ISO 8601) |
| linkId | string | No | Link UUID from the backend |
| Field | Type | Required | Description |
|---|---|---|---|
| installId | string | Yes | Server-assigned install UUID |
| attributed | boolean | Yes | Whether install was matched to a click |
| confidenceScore | number | Yes | Match confidence (0-100) |
| matchedFactors | string[] | Yes | Which fingerprint factors matched |
| deepLinkData | DeepLinkData | No | Link data if attributed (null/empty if organic) |
| Field | Type | Required | Description |
|---|---|---|---|
| templateId | string | No | Template UUID (auto-selected if omitted) |
| templateSlug | string | No | Template slug (for URL construction) |
| deepLinkParameters | map<string, string> | No | In-app routing parameters |
| title | string | No | Link title |
| description | string | No | Link description |
| customCode | string | No | Custom short code |
| utmParameters | UTMParameters | No | Campaign tracking parameters |
| externalUserId | string | No | Identifier for the app user creating the link (enables per-user deduplication and share attribution) |
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Full shareable URL |
| shortCode | string | Yes | Generated short code |
| linkId | string | Yes | Link UUID |
| deduplicated | boolean | No | True if an existing link was returned instead of creating a new one |
| Field | Type | Required |
|---|---|---|
| source | string | No |
| medium | string | No |
| campaign | string | No |
| term | string | No |
| content | string | No |
| Field | Type | Required | Description |
|---|---|---|---|
| userAgent | string | Yes | App/version + OS/version |
| timezone | string | No | IANA timezone identifier |
| language | string | No | Device language/locale |
| screenWidth | number | No | Native screen width in pixels |
| screenHeight | number | No | Native screen height in pixels |
| platform | string | No | iOS, Android, etc. |
| platformVersion | string | No | OS version string |
| appVersion | string | No | Host app version |
| deviceId | string | No | IDFA/GAID (only if user consented) |
| attributionWindowHours | number | No | Attribution window in hours |
Status of each feature across SDKs. Update this table when adding features or SDKs.
| # | Feature | iOS | React Native | Android | Expo |
|---|---|---|---|---|---|
| 1 | Initialization | Done | Done | Done | Done |
| 2 | Deferred deep linking | Done | Done | Done | Done |
| 3 | Direct deep linking | Done | Done | Done | Done |
| 4 | Server-side URL resolution | Done | Done | Done | Done |
| 5 | Event tracking | Done | Done | Done | Done |
| 6 | Revenue tracking | Done | Done | Done | Done |
| 7 | Link creation | Done | Done | Done | Done |
| 8 | User identity | Done | Done | Done | Done |
| 9 | Attribution data access | Done | Partial | Done | Done |
| 10 | Data management | Done | Partial | Done | Done |
| 11 | Configuration validation | Done | Missing | Done | Done |
| 12 | Error handling | Done | Partial | Done | Done |
| 13 | Offline resilience (event queue) | Done | Missing | Done | Done |
- React Native -- Attribution data access:
isFirstLaunch()is private, not exposed to consumers - React Native -- Data management: No
reset()method (onlyclearData()) - React Native -- Error handling: Uses generic
Errorthrows instead of typed error cases - React Native -- Configuration validation: No HTTPS enforcement or attribution window bounds checking
- React Native -- Offline resilience: Events are fire-and-forget; no queue, no retry on failure
We welcome community SDKs for any platform. Before an SDK can be listed on the LinkForty docs, we verify it works end-to-end.
- Repository link and intended package registry name (npm, pub.dev, Maven, CocoaPods, etc.)
- Example app or
/examplefolder demonstrating the core flows: initialization, deferred deep linking, direct deep linking, and link creation - CI pipeline with linting and tests (GitHub Actions or equivalent)
- Install and quickstart documentation in the README
| Tier | Requirements | Listed as |
|---|---|---|
| Community | Passes end-to-end verification, meets submission requirements | "Community SDK" with link to your repo |
| Official | Multiple stable releases, co-maintained with LinkForty team, transferred to the LinkForty GitHub org | "Official SDK" with full documentation |
To get started, open an issue on GitHub to coordinate with the team and avoid duplicate work.