Open-source alternative to Branch.io, AppsFlyer OneLink, and Firebase Dynamic Links
Self-hosted deep linking engine with device detection, analytics, deferred deep linking, and smart routing. No per-click pricing, no vendor lock-in, full data ownership — runs on your own PostgreSQL. Firebase Dynamic Links shut down in August 2025; LinkForty is a production-ready, open-source replacement you can deploy today.
- Self-hosted and open-source — AGPL-3.0 licensed, deploy on your own infrastructure
- No per-click pricing — No usage-based fees, no monthly minimums, no enterprise sales calls
- Full data ownership — All click data, analytics, and attribution stored in your PostgreSQL database
- Privacy-first — No third-party data sharing, no tracking pixels, your users' data stays with you
- Drop-in replacement — REST API + mobile SDKs for React Native, Expo, iOS (Swift), and Android (Kotlin)
- Firebase Dynamic Links replacement — Google shut down Firebase Dynamic Links in August 2025. LinkForty provides the same capabilities with a self-hosted, open-source stack
| Feature | LinkForty Core | Branch | AppsFlyer | Firebase Dynamic Links |
|---|---|---|---|---|
| Open Source | Yes (AGPL-3.0) | No | No | No |
| Self-Hosted | Yes | No | No | No |
| Data Ownership | Complete | Vendor-controlled | Vendor-controlled | Was Google-controlled |
| Deferred Deep Linking | Yes | Yes | Yes | Was supported |
| Device Detection & Routing | Yes | Yes | Yes | Was supported |
| Click Analytics | Yes | Yes | Yes | Basic |
| QR Code Generation | Built-in | No | No | No |
| Webhooks | Yes | Enterprise only | Enterprise only | No |
| iOS Universal Links | Yes | Yes | Yes | Was supported |
| Android App Links | Yes | Yes | Yes | Was supported |
| UTM Parameter Tracking | Yes | Yes | Custom params | Was supported |
| Custom Domains | Yes | Enterprise only | Enterprise only | No |
- Smart Link Routing - Create short links with device-specific URLs for iOS, Android, and web
- Device Detection - Automatic detection and routing based on user device
- Click Analytics - Track clicks with geolocation, device type, platform, and more
- UTM Parameters - Built-in support for UTM campaign tracking
- Targeting Rules - Filter by country, device, and language before redirecting
- QR Code Generation - Generate QR codes (PNG/SVG) for any link
- Deferred Deep Linking - Probabilistic fingerprint matching for install attribution
- Webhooks - Event-driven integrations with HMAC-signed payloads and retry logic
- Smart App Opening - Mobile clicks serve an interstitial that tries the app via URI scheme, falls back to the App Store / Play Store. Preserves URL fragments for E2E encryption keys
- OG Preview Pages - Social media scraper detection with Open Graph meta tags
- iOS Universal Links & Android App Links - Serve
.well-knownfiles automatically - Link Expiration - Set expiration dates for time-sensitive links
- Redis Caching - Optional Redis support for high-performance link lookups
- PostgreSQL Storage - Reliable data persistence with full SQL capabilities
- TypeScript - Fully typed API for better developer experience
- No Auth Included - Bring your own authentication;
userIdis optional for multi-tenant scoping
npm install @linkforty/coreimport { createServer } from '@linkforty/core';
async function start() {
const server = await createServer({
database: {
url: 'postgresql://localhost/linkforty',
},
redis: {
url: 'redis://localhost:6379',
},
});
await server.listen({ port: 3000, host: '0.0.0.0' });
console.log('Server running on http://localhost:3000');
}
start();Quick Start:
# Pull the latest image
docker pull linkforty/core:latest
# Run with Docker Compose
curl -O https://raw.githubusercontent.com/linkforty/core/main/docker-compose.yml
docker compose up -dOr use Docker CLI:
docker run -d \
--name linkforty \
-p 3000:3000 \
-e DATABASE_URL=postgresql://user:pass@host:5432/linkforty?sslmode=disable \
-e REDIS_URL=redis://host:6379 \
linkforty/core:latestFeatures:
- Pre-built multi-architecture images (AMD64 + ARM64)
- Automatic updates with version tags
- Non-root user for security
- Built-in health checks
- Supply chain attestations (SBOM + Provenance)
See DOCKER.md for complete deployment guide.
userId is optional. When provided, the link is scoped to that user (multi-tenant mode). When omitted, the link has no owner (single-tenant mode).
POST /api/links
Content-Type: application/json
{
"userId": "user-uuid",
"originalUrl": "https://example.com",
"title": "My Link",
"description": "Summer campaign link",
"iosAppStoreUrl": "https://apps.apple.com/app/id123456",
"androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.example",
"webFallbackUrl": "https://example.com/product/123",
"appScheme": "myapp",
"iosUniversalLink": "https://example.com/app/product/123",
"androidAppLink": "https://example.com/app/product/123",
"deepLinkPath": "/product/123",
"deepLinkParameters": { "ref": "campaign-1" },
"utmParameters": {
"source": "twitter",
"medium": "social",
"campaign": "summer-sale"
},
"ogTitle": "Check out this deal",
"ogDescription": "50% off summer sale",
"ogImageUrl": "https://example.com/og-image.png",
"targetingRules": {
"countries": ["US", "CA"],
"devices": ["ios", "android"],
"languages": ["en"]
},
"attributionWindowHours": 168,
"customCode": "summer-sale",
"expiresAt": "2026-12-31T23:59:59Z"
}All fields except originalUrl are optional.
# Single-tenant (all links)
GET /api/links
# Multi-tenant (scoped to user)
GET /api/links?userId=user-uuidGET /api/links/:id
GET /api/links/:id?userId=user-uuidPUT /api/links/:id?userId=user-uuid
Content-Type: application/json
{
"title": "Updated Title",
"isActive": false
}POST /api/links/:id/duplicate?userId=user-uuidDELETE /api/links/:id?userId=user-uuid# All links
GET /api/analytics/overview?days=30
# Scoped to user
GET /api/analytics/overview?userId=user-uuid&days=30Returns: totalClicks, uniqueClicks, clicksByDate, clicksByCountry, clicksByDevice, clicksByPlatform, topLinks
GET /api/analytics/links/:linkId?days=30GET /:shortCode
GET /:templateSlug/:shortCodeAutomatically redirects users to the appropriate URL based on device type (iOS/Android/web), evaluates targeting rules, and tracks the click asynchronously.
Mobile interstitial: When a link has appScheme configured and a store fallback URL (iOS App Store or Google Play), mobile requests receive a smart interstitial page instead of a raw 302 redirect. The interstitial tries to open the app via URI scheme and falls back to the app store after 1.5 seconds. This handles the case where a 302 to a custom URI scheme fails silently when the app is not installed. URL fragments are preserved through the redirect, enabling patterns like E2E encryption where the decryption key lives in the fragment.
Launchpad page (desktop): A link to app-only content has nowhere to send a desktop visitor. When the chain resolves no web destination (link → template → workspace appConfig), a desktop request gets a hosted landing page instead of an error: the link's title, description and image (ogTitle / ogDescription / ogImageUrl, falling back to title / description), the app's icon and name, App Store / Google Play buttons, and a QR code so the visitor can finish on their phone. The page is noindex, sent with Cache-Control: no-store and a content-security policy that allows no script without a per-response nonce, and it never navigates on its own — no timers, no automatic scheme attempts.
When the link does have a web destination, the page offers it as a "Continue on the web" link, so a visitor is never trapped. By default it appears only where the alternative is nothing; a link that resolves to a destination still gets its 302. Two knobs change that:
-
organizations.settings.launchpad(per workspace):{ "launchpad": { "desktop": "no-destination", // "no-destination" (default) | "always" | "off" "mobile": "store", // "store" (default) | "page" "appName": "Ride Alert", "appIconUrl": "https://cdn.example/icon.png", "accentColor": "#0f766e", // buttons, the web link, focus rings "backgroundColor": "#101418" // optional; fixes the page's palette (see below) } }Without
backgroundColorthe page follows the visitor's light/dark preference. With one, the background is fixed and the text, surfaces and borders are derived from it — a light or a dark brand color both stay legible, and the visitor's theme no longer changes the page. -
links.launchpad_mode(per link,launchpadModeon the links API):inherit(default),onoroff. The link's value wins over the workspace's.
With desktop: "off" (or launchpad_mode: "off"), a link with no web destination gets the plain "This link opens in an app" page instead.
Launchpad page (mobile): With mobile: "page", a phone visitor whose platform has no Universal Link / App Link on the link gets the same page instead of the store redirect or the scheme interstitial: an "Open in app" button when the link has a URI scheme (a tap, not a navigation — the URL fragment is appended on tap), the visitor's own store button, and the web fallback as a "Continue on the web" link. That last one matters inside in-app browsers (Gmail, Facebook, Instagram…), which bypass Universal Links: the store redirect would send someone who has the app to the store, so the redirect path prefers the web fallback there to give the OS a second chance on the next hop — and the page keeps that hop available as a tap. Links that carry a Universal Link / App Link keep their 302 regardless, because the OS resolves the installed case before the request reaches this server. launchpad_mode: "off" opts a link out; "on" does not force the page onto a workspace that chose "store". The default, "store", is unchanged behaviour.
Two hooks on RedirectRouteOptions let a host application extend the page without forking it — see Server Options.
GET /api/links/:id/qr?format=png&size=300
GET /api/links/:id/qr?format=svg
GET /api/links/:id/qr?url=https://links.example.com/spring/abc123 # encode the link on a custom host / template pathWithout url the code encodes SHORTLINK_DOMAIN/{shortCode}. url must be an http(s) URL whose path ends in the link's short code; anything else is a 400. The Launchpad page uses it to encode its own address, so a phone that scans the code lands on the same host and path the visitor is on.
GET /api/webhooks?userId=user-uuid
POST /api/webhooks # Body: { name, url, events, userId? }
GET /api/webhooks/:id?userId=user-uuid
PUT /api/webhooks/:id?userId=user-uuid
DELETE /api/webhooks/:id?userId=user-uuid
POST /api/webhooks/:id/test?userId=user-uuidEvents: click_event, install_event, conversion_event, sdk_event. Payloads are HMAC SHA-256 signed.
POST /api/sdk/v1/install # Report app install, get deferred deep link
GET /api/sdk/v1/attribution/:fingerprint # Debug attribution lookups
POST /api/sdk/v1/event # Track in-app conversion events
GET /api/sdk/v1/resolve/:shortCode # Resolve link to deep link data (no redirect)
GET /api/sdk/v1/health # Health checkGET /health # Liveness — process is up (no DB access)
GET /health/ready # Readiness — 503 if the database is unreachablePOST /api/debug/simulate # Simulate a link click with custom parameters
WS /api/debug/live?userId=user-uuid # WebSocket live click event stream
GET /api/debug/user-agents # Common UA strings for testing
GET /api/debug/countries # Common countries list
GET /api/debug/languages # Common languages listGET /.well-known/apple-app-site-association # iOS Universal Links
GET /.well-known/assetlinks.json # Android App LinksGET /:shortCode/preview # OG meta tag page for social scrapersinterface ServerOptions {
database?: {
url?: string; // PostgreSQL connection string
pool?: {
min?: number; // Minimum pool connections (default: 2)
max?: number; // Maximum pool connections (default: 10)
};
};
redis?: {
url: string; // Redis connection string (optional)
};
cors?: {
origin: string | string[]; // CORS allowed origins (default: '*')
};
logger?: boolean; // Enable Fastify logger (default: true)
trustProxy?: boolean | number; // Trust X-Forwarded-For when behind a proxy (default: false)
redirect?: RedirectRouteOptions; // See below
}
interface RedirectRouteOptions {
abuseReportUrl?: string; // Linked from the link-safety warning page
launchpad?: {
// Supply richer content for the launchpad page than the link row carries:
// a rendered share image, a templated hero. Return null for the default.
// A hook that throws is logged and treated as null — the page never fails
// because of it. `heroHtml` is inserted unescaped: render it yourself,
// never from end-user input.
resolveContent?: (link, settings) => Promise<LaunchpadContent | null>;
// Endpoint that receives `{ linkId, event }` beacons from the page —
// "view" on load, "cta_ios" / "cta_android" / "cta_open" on tap. No
// cookies or identifiers. Unset means the page sends nothing.
beaconUrl?: string;
};
}renderLaunchpadPage(), defaultLaunchpadContent() and shouldServeLaunchpadOnDesktop() are exported so a host can render the same page elsewhere (a settings preview, for instance).
When Core runs behind a reverse proxy, CDN, or load balancer, set trustProxy so the server uses the real client IP from X-Forwarded-For for redirect targeting, geo, attribution, and fingerprinting. Pass it when creating the server (e.g. trustProxy: true or a number of proxy hops) or set the TRUST_PROXY environment variable (e.g. TRUST_PROXY=1). Client-provided ipAddress in the SDK install request body is not used as the trusted IP; it is optional debug metadata only and must not be relied on for attribution.
DATABASE_URL=postgresql://localhost/linkforty
REDIS_URL=redis://localhost:6379
PORT=3000
NODE_ENV=production
CORS_ORIGIN=*
# When behind a reverse proxy: TRUST_PROXY=1 (or number of hops) so client IP is read from X-Forwarded-For
# TRUST_PROXY=1
# Mobile SDK (optional — for iOS Universal Links and Android App Links)
IOS_TEAM_ID=ABC123XYZ
IOS_BUNDLE_ID=com.yourcompany.yourapp
ANDROID_PACKAGE_NAME=com.yourcompany.yourapp
ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:DD:...
# Custom domain for QR code URLs (optional)
SHORTLINK_DOMAIN=yourdomain.comCore does not create a users table. Authentication and user management are the consumer's responsibility. The user_id column on links and webhooks is optional (nullable, no foreign key) — use it for multi-tenant scoping when your auth layer provides a user identity.
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| user_id | UUID | Optional owner/tenant identifier |
| short_code | VARCHAR(20) | Unique short code |
| original_url | TEXT | Original URL |
| title | VARCHAR(255) | Link title |
| description | TEXT | Link description |
| ios_app_store_url | TEXT | iOS App Store URL |
| android_app_store_url | TEXT | Android Play Store URL |
| web_fallback_url | TEXT | Web fallback URL |
| app_scheme | VARCHAR(255) | URI scheme (e.g., "myapp") |
| ios_universal_link | TEXT | iOS Universal Link URL |
| android_app_link | TEXT | Android App Link URL |
| deep_link_path | TEXT | In-app destination path |
| deep_link_parameters | JSONB | Custom app parameters |
| utm_parameters | JSONB | UTM tracking parameters |
| targeting_rules | JSONB | Country/device/language targeting |
| og_title | VARCHAR(255) | Open Graph title |
| og_description | TEXT | Open Graph description |
| og_image_url | TEXT | Open Graph image URL |
| og_type | VARCHAR(50) | Open Graph type (default: "website") |
| launchpad_mode | VARCHAR(10) | Launchpad page: inherit / on / off |
| attribution_window_hours | INTEGER | Install attribution window (default: 168) |
| is_active | BOOLEAN | Active status |
| expires_at | TIMESTAMP | Expiration date |
| created_at | TIMESTAMP | Creation timestamp |
| updated_at | TIMESTAMP | Last update timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| link_id | UUID | Foreign key to links |
| clicked_at | TIMESTAMP | Click timestamp |
| ip_address | INET | User IP address |
| user_agent | TEXT | User agent string |
| device_type | VARCHAR(20) | Device type (ios/android/web) |
| platform | VARCHAR(20) | Platform (iOS/Android/Web) |
| country_code | CHAR(2) | Country code |
| country_name | VARCHAR(100) | Country name |
| region | VARCHAR(100) | Region/state |
| city | VARCHAR(100) | City |
| latitude | DECIMAL | Latitude |
| longitude | DECIMAL | Longitude |
| timezone | VARCHAR(100) | Timezone |
| utm_source | VARCHAR(255) | UTM source |
| utm_medium | VARCHAR(255) | UTM medium |
| utm_campaign | VARCHAR(255) | UTM campaign |
| referrer | TEXT | Referrer URL |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| click_id | UUID | Foreign key to click_events |
| fingerprint_hash | VARCHAR(64) | SHA-256 hash of fingerprint signals |
| ip_address | INET | IP address |
| user_agent | TEXT | User agent string |
| timezone | VARCHAR(100) | Timezone |
| language | VARCHAR(10) | Browser language |
| screen_width | INTEGER | Screen width |
| screen_height | INTEGER | Screen height |
| platform | VARCHAR(50) | Platform |
| platform_version | VARCHAR(50) | Platform version |
| created_at | TIMESTAMP | Creation timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| link_id | UUID | Attributed link (nullable) |
| click_id | UUID | Attributed click (nullable) |
| fingerprint_hash | VARCHAR(64) | Device fingerprint hash |
| confidence_score | DECIMAL | Match confidence (0-100) |
| installed_at | TIMESTAMP | Install timestamp |
| first_open_at | TIMESTAMP | First app open |
| deep_link_retrieved | BOOLEAN | Whether deferred link was fetched |
| deep_link_data | JSONB | Deferred deep link data |
| attribution_window_hours | INTEGER | Attribution window used (default: 168) |
| device_id | VARCHAR(255) | Optional device identifier |
| created_at | TIMESTAMP | Creation timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| install_id | UUID | Foreign key to install_events |
| event_name | VARCHAR(255) | Event name |
| event_data | JSONB | Custom event properties |
| event_timestamp | TIMESTAMP | When the event occurred |
| created_at | TIMESTAMP | Creation timestamp |
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| user_id | UUID | Optional owner/tenant identifier |
| name | VARCHAR(255) | Webhook name |
| url | TEXT | Delivery URL |
| secret | VARCHAR(255) | HMAC signing secret |
| events | TEXT[] | Subscribed event types |
| is_active | BOOLEAN | Active status |
| retry_count | INTEGER | Max retries (default: 3) |
| timeout_ms | INTEGER | Request timeout (default: 10000) |
| headers | JSONB | Custom HTTP headers |
| created_at | TIMESTAMP | Creation timestamp |
| updated_at | TIMESTAMP | Last update timestamp |
import { generateShortCode } from '@linkforty/core';
const code = generateShortCode(8); // Returns 8-character nanoidimport { detectDevice } from '@linkforty/core';
const device = detectDevice(userAgent); // Returns 'ios' | 'android' | 'web'import { getLocationFromIP } from '@linkforty/core';
const location = getLocationFromIP('8.8.8.8');
// Returns: { countryCode, countryName, region, city, latitude, longitude, timezone }import { buildRedirectUrl } from '@linkforty/core';
const url = buildRedirectUrl('https://example.com', {
source: 'twitter',
medium: 'social',
campaign: 'summer-sale'
});
// Returns: https://example.com?utm_source=twitter&utm_medium=social&utm_campaign=summer-saleimport { createServer } from '@linkforty/core';
const server = await createServer({
database: { url: 'postgresql://localhost/linkforty' },
});
// Add custom routes
server.get('/custom', async (request, reply) => {
return { message: 'Hello World' };
});
await server.listen({ port: 3000 });import Fastify from 'fastify';
import { initializeDatabase, redirectRoutes, linkRoutes } from '@linkforty/core';
const fastify = Fastify();
// Initialize database separately
await initializeDatabase({ url: 'postgresql://localhost/linkforty' });
// Register only specific routes
await fastify.register(redirectRoutes);
await fastify.register(linkRoutes);
await fastify.listen({ port: 3000 });LinkForty can be deployed in multiple ways depending on your needs:
Deploy to managed platforms with minimal DevOps overhead:
Fly.io (Recommended)
- Global edge deployment
- Managed PostgreSQL and Redis
- Auto-scaling and SSL included
- Starting at ~$10-15/month
See infra/ directory for all deployment options and platform-specific guides.
Production-ready Docker images available on Docker Hub:
# One-command deployment
curl -O https://raw.githubusercontent.com/linkforty/core/main/docker-compose.yml
docker compose up -dImage Details:
- Registry:
linkforty/core - Tags:
latest,v1.x.x,main - Architectures: linux/amd64, linux/arm64
- Base: Node.js 22 Alpine (minimal, secure)
- Security: Non-root user, SBOM attestations
Version Pinning (Recommended):
services:
linkforty:
image: linkforty/core:v1.5.0 # Pin to specific versionSee DOCKER.md for complete deployment guide including:
- Environment configuration
- Health checks
- Backup strategies
- Production best practices
For custom infrastructure needs:
- Install dependencies:
npm install @linkforty/core - Set up PostgreSQL database (13+)
- Set up Redis (optional but recommended)
- Run migrations:
npm run migrate - Start server:
node server.js
Community-maintained templates available for:
- AWS (ECS/Fargate)
- Google Cloud Run
- Railway, Render, and more
See infra/CONTRIBUTING.md to add support for additional platforms.
- Redis caching: 5-minute TTL on link lookups reduces database queries by 90%
- Database indexes: Optimized queries for fast link lookups and analytics
- Async click tracking: Non-blocking click event logging via
setImmediate() - Connection pooling: Efficient database connection management (min 2, max 10)
- SQL injection protection: Parameterized queries throughout
- Input validation: Zod schema validation on all inputs
- CORS configuration: Configurable CORS for API access control
- Link expiration: Automatic handling of expired links
- Webhook signing: HMAC SHA-256 signed payloads
- No auth included: Core does not include authentication. The optional
userIdparameter provides data scoping but does not verify identity. Add your own auth middleware as needed.
LinkForty Core supports iOS Universal Links and Android App Links for seamless deep linking in mobile applications.
-
Set environment variables:
IOS_TEAM_ID=ABC123XYZ # Your Apple Developer Team ID IOS_BUNDLE_ID=com.yourcompany.yourapp -
Configure in Xcode:
- Add "Associated Domains" capability
- Add domain:
applinks:yourdomain.com
-
Verify AASA file:
curl https://yourdomain.com/.well-known/apple-app-site-association
Expected response:
{ "applinks": { "apps": [], "details": [ { "appID": "ABC123XYZ.com.yourcompany.yourapp", "paths": ["*"] } ] } }
-
Get your SHA-256 fingerprint:
# Debug keystore keytool -list -v -keystore ~/.android/debug.keystore \ -alias androiddebugkey -storepass android -keypass android # Release keystore keytool -list -v -keystore /path/to/release.keystore \ -alias your-alias
-
Set environment variables:
ANDROID_PACKAGE_NAME=com.yourcompany.yourapp ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:DD:... # Multiple fingerprints (debug + release) ANDROID_SHA256_FINGERPRINTS=AA:BB:CC:...,DD:EE:FF:... -
Configure in AndroidManifest.xml:
<intent-filter android:autoVerify="true"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" /> <data android:host="yourdomain.com" /> </intent-filter>
-
Verify assetlinks.json:
curl https://yourdomain.com/.well-known/assetlinks.json
Expected response:
[ { "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": ["AA:BB:CC:..."] } } ]
| Platform | Package | Install |
|---|---|---|
| React Native | @linkforty/mobile-sdk-react-native |
npm install @linkforty/mobile-sdk-react-native |
| Expo | @linkforty/mobile-sdk-expo |
npx expo install @linkforty/mobile-sdk-expo |
| iOS (Swift) | LinkFortySDK | Swift Package Manager |
| Android (Kotlin) | LinkFortySDK | Gradle dependency |
See the SDK documentation for integration guides.
Test iOS Universal Links with Apple's validator:
https://search.developer.apple.com/appsearch-validation-tool/Test Android App Links with Google's validator:
adb shell am start -a android.intent.action.VIEW \
-d "https://yourdomain.com/test"Switching from an existing deep linking provider? LinkForty supports zero-downtime migration via custom domain DNS cutover.
- Migrate from Branch.io
- Migrate from AppsFlyer OneLink
- Migrate from Firebase Dynamic Links (shut down August 2025)
- Migrate from Adjust
- Migrate from Kochava
- Migration overview and checklist
LinkForty provides machine-readable documentation for AI coding assistants (Claude, ChatGPT, Cursor, Copilot).
- Quick reference: docs.linkforty.com/llms.txt
- Complete integration guide: docs.linkforty.com/llms-full.txt
Download into your project for AI-assisted integration:
curl -o LINKFORTY.md https://docs.linkforty.com/llms-full.txtThe npm package also ships with an llms.txt file — AI tools that read from node_modules can discover it automatically.
Contributions are welcome! Please see CONTRIBUTING.md for details.
AGPL-3.0 - see LICENSE file for details.
- @linkforty/mobile-sdk-react-native - React Native SDK
- @linkforty/mobile-sdk-expo - Expo SDK
- mobile-sdk-ios - iOS SDK (Swift)
- mobile-sdk-android - Android SDK (Kotlin)
- LinkForty Cloud - Hosted SaaS version with authentication, teams, billing, and dashboard
- Documentation: https://docs.linkforty.com
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Fastify - Fast web framework
- PostgreSQL - Powerful database
- Redis - In-memory cache
- Zod - TypeScript-first schema validation
- nanoid - Unique ID generation
- geoip-lite - IP geolocation
- ua-parser-js - User agent parsing
- qrcode - QR code generation