Skip to main content

Create Link

Create a new short link with platform-specific routing, attribution tracking, and UTM parameters.

Core vs Cloud

Core and Cloud accept different fields. Core requires userId and originalUrl. Cloud requires either templateId or templateSlug, derives userId from your API key, and does not accept originalUrl — its web destination is webFallbackUrl. See examples for both below.

Endpoint​

POST /api/links

Authentication​

  • Core (Self-Hosted): Include userId in the request body. See Authentication.
  • Cloud: Requires a valid API key in the Authorization header. See Authentication.

Request Body​

Core Fields​

FieldTypeRequiredDescription
userIdstring (UUID)YesUser ID for link ownership
originalUrlstring (URL)YesDestination URL (fallback)
templateIdstring (UUID)NoTemplate to use for default values
customCodestringNoCustom short code
titlestringNoInternal title
descriptionstringNoInternal description
iosAppStoreUrlstring (URL)NoiOS App Store URL
androidAppStoreUrlstring (URL)NoAndroid Play Store URL
webFallbackUrlstring (URL)NoDestination for web users
appSchemestringNoCustom URL scheme, without :// (e.g. myapp)
iosUniversalLinkstring (URL)NoiOS Universal Link URL
androidAppLinkstring (URL)NoAndroid App Link URL
deepLinkPathstringNoDeep link path within the app
deepLinkParametersobjectNoKey-value pairs passed to the app
attributionWindowHoursnumberNoAttribution window (1-2160 hours). Default: 168 (7 days)
utmParametersobjectNoUTM parameters to append
targetingRulesobjectNoGeographic/language/device targeting
ogTitlestringNoOpen Graph title for social previews
ogDescriptionstringNoOpen Graph description
ogImageUrlstring (URL)NoOpen Graph image URL
ogTypestringNoOpen Graph type (e.g. website)
expiresAtstring (ISO 8601)NoExpiration timestamp

Cloud Fields​

FieldTypeRequiredDescription
templateIdstring (UUID)See noteTemplate to use for default values
templateSlugstringSee noteTemplate slug, as an alternative to templateId
titlestringNoInternal title for organization
descriptionstringNoInternal description
iosAppStoreUrlstring (URL)NoDestination for iOS users
androidAppStoreUrlstring (URL)NoDestination for Android users
webFallbackUrlstring (URL)NoDestination for web users (http/https only)
appSchemestringNoCustom URL scheme, lowercase, without :// (e.g. myapp)
iosUniversalLinkstring (URL)NoiOS Universal Link URL
androidAppLinkstring (URL)NoAndroid App Link URL
deepLinkPathstringNoDeep link path within the app
customSchemeUrlstringNoFull custom-scheme destination (e.g. myapp://product/123)
deepLinkParametersobjectNoKey-value pairs passed to the app
customCodestringNoCustom short code
projectIdstring (UUID) | nullNoProject to assign the link to. null clears the assignment
domainIdstring (UUID) | nullNoCustom domain to issue the link on. null follows the workspace default domain (the link is not pinned to it, so changing the default later moves the link too)
utmParametersobjectNoUTM parameters to append
targetingRulesobjectNoGeographic/language/device targeting
attributionWindowHoursnumberNoInstall attribution window, 1-2160 hours
eventAttributionWindowHoursnumberNoIn-app event attribution window, 1-2160 hours
expiresAtstring (ISO 8601)NoExpiration timestamp
appendClickIdbooleanNoAppend ?lf_click=<id> to web destinations. Defaults to true on Cloud
ogTitlestring (max 255)NoOpen Graph title for this link's share preview
ogDescriptionstring (max 500)NoOpen Graph description
ogImageUrlstring (URL)NoOpen Graph image URL, http(s) only
ogTypestringNoOpen Graph type. Defaults to website
launchpadModeinherit | on | offNoPer-link Launchpad override. inherit (default) follows the workspace setting; on shows the page to desktop visitors even when the link has a web destination; off never shows it
Per-link previews for user-shared content

The og* fields override the workspace and template share cards for that one link, so a link a user creates to share a specific item can unfurl with that item's own title and image. Leave them unset and the link uses the card configured in the dashboard. See Social Share Preview for the full resolution order.

Cloud requires a template

Every Cloud link belongs to a template, but you may identify it with either templateId or templateSlug — one of the two is required. Templates define default settings for your links. See Link Templates.

Unknown fields are silently ignored

The request schema discards field names it does not recognise instead of rejecting them, so a misspelled or non-existent field produces a successful response with that value dropped. If a link is created without a destination you set, check the field name against this table first.

In particular, Cloud does not accept originalUrl, iosUrl, or androidUrl. Those are Core field names — the Cloud equivalents are webFallbackUrl, iosAppStoreUrl, and androidAppStoreUrl.

UTM Parameters Object​

{
source?: string; // Traffic source (e.g., "instagram")
medium?: string; // Marketing medium (e.g., "social")
campaign?: string; // Campaign name (e.g., "spring-sale-2024")
term?: string; // Paid keywords (e.g., "wireless-headphones")
content?: string; // Content variant (e.g., "carousel-ad-1")
}

Targeting Rules Object​

{
countries?: string[]; // ISO 3166-1 alpha-2 codes (e.g., ["US", "CA", "GB"])
languages?: string[]; // ISO 639-1 codes (e.g., ["en", "es", "fr"])
devices?: string[]; // ["ios", "android", "web"]
}

Response​

Success Response​

Status Code: 201 Created

Core response:

{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"short_code": "abc123",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"original_url": "https://example.com/products/wireless-headphones",
"title": "Wireless Headphones - Spring Sale",
"description": "Q2 2024 Instagram campaign",
"ios_app_store_url": "https://apps.apple.com/app/your-store/id123456789",
"android_app_store_url": "https://play.google.com/store/apps/details?id=com.yourstore",
"web_fallback_url": "https://example.com/products/wireless-headphones?platform=web",
"attribution_window_hours": 168,
"utm_parameters": {
"source": "instagram",
"medium": "social",
"campaign": "spring-sale-2024"
},
"targeting_rules": {
"countries": ["US", "CA", "GB"],
"languages": ["en"],
"devices": ["ios", "android"]
},
"is_active": true,
"expires_at": null,
"created_at": "2024-01-20T15:30:00.000Z",
"updated_at": "2024-01-20T15:30:00.000Z"
}

Cloud response includes additional fields:

{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"short_code": "abc123",
"short_url": "https://acme.toapp.me/spring-sale/abc123",
"domain": null,
"user_id": "user_123",
"organization_id": "org_456",
"project_id": null,
"template_id": "template_789",
"original_url": null,
"title": "Wireless Headphones - Spring Sale",
"ios_app_store_url": "https://apps.apple.com/app/your-store/id123456789",
"android_app_store_url": "https://play.google.com/store/apps/details?id=com.yourstore",
"web_fallback_url": "https://example.com/products/wireless-headphones?platform=web",
"attribution_window_hours": 168,
"utm_parameters": {
"source": "instagram",
"medium": "social",
"campaign": "spring-sale-2024"
},
"is_active": true,
"expires_at": null,
"created_at": "2024-01-20T15:30:00.000Z",
"updated_at": "2024-01-20T15:30:00.000Z"
}

short_url is the URL to hand out. It is resolved on every response — the link's assigned custom domain, else the workspace's default verified domain, else the workspace's own LinkForty domain once that is its default, else the platform domain — and includes the template slug when the link was created from a template. domain is only the explicitly assigned domain: it is null when the link follows the workspace default, even though short_url already uses that default.

Error Responses​

400 Bad Request - Invalid URL​

{
"error": "Bad Request",
"message": "Invalid URL format",
"statusCode": 400,
"validation": {
"originalUrl": "Must be a valid URL"
}
}

400 Bad Request - Invalid Attribution Window​

{
"error": "Bad Request",
"message": "Attribution window must be between 1 and 2160 hours",
"statusCode": 400,
"validation": {
"attributionWindowHours": "Attribution window must be at least 1 hour"
}
}

409 Conflict - Custom Code Already Exists​

{
"error": "Conflict",
"message": "Short code 'spring-sale' is already in use",
"statusCode": 409
}

401 Unauthorized (Cloud)​

{
"error": "Unauthorized",
"message": "Missing or invalid API key",
"statusCode": 401
}

404 Not Found - Template Not Found (Cloud)​

{
"error": "Not Found",
"message": "Template not found",
"statusCode": 404
}

Examples​

curl -X POST https://your-domain.com/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/product/123"
}'

Core - With Platform-Specific URLs​

curl -X POST https://your-domain.com/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/product/123",
"title": "Product 123 Campaign",
"iosAppStoreUrl": "https://apps.apple.com/app/your-store/id123456789",
"androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.yourstore",
"deepLinkPath": "/product/123"
}'

Core - With Deep Linking​

curl -X POST https://your-domain.com/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/product/123",
"appScheme": "myapp://",
"iosUniversalLink": "https://myapp.com/product/123",
"androidAppLink": "https://myapp.com/product/123",
"deepLinkPath": "/product/123",
"deepLinkParameters": {
"productId": "123",
"source": "campaign"
}
}'
curl -X POST https://api.linkforty.com/api/links \
-H "Authorization: Bearer dl_a1b2c3d4e5f6a7b8" \
-H "Content-Type: application/json" \
-d '{
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"webFallbackUrl": "https://example.com/product/123"
}'

Cloud - With Platform-Specific URLs​

curl -X POST https://api.linkforty.com/api/links \
-H "Authorization: Bearer dl_a1b2c3d4e5f6a7b8" \
-H "Content-Type: application/json" \
-d '{
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"webFallbackUrl": "https://example.com/product/123",
"title": "Product 123 Campaign",
"iosAppStoreUrl": "https://apps.apple.com/app/your-store/id123456789",
"androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.yourstore"
}'

With UTM Parameters​

curl -X POST https://your-domain.com/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/product/123",
"utmParameters": {
"source": "facebook",
"medium": "cpc",
"campaign": "summer-2024",
"term": "wireless-headphones",
"content": "image-ad-1"
}
}'

With Attribution Window​

curl -X POST https://your-domain.com/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/product/123",
"attributionWindowHours": 336
}'

Attribution Window: 336 hours = 14 days

With Targeting Rules​

curl -X POST https://your-domain.com/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/product/123",
"targetingRules": {
"countries": ["US", "CA"],
"languages": ["en"],
"devices": ["ios", "android"]
}
}'

Behavior: Only users in US/Canada, with English language settings, on mobile devices will be redirected. Others see default URL.

With Expiration​

curl -X POST https://your-domain.com/api/links \
-H "Content-Type: application/json" \
-d '{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/flash-sale",
"title": "24-Hour Flash Sale",
"expiresAt": "2024-12-31T23:59:59Z"
}'

Use Cases​

{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://shop.example.com/products/wireless-headphones",
"title": "Wireless Headphones Product Page",
"iosAppStoreUrl": "https://apps.apple.com/app/shop-app/id123",
"androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.shop",
"deepLinkPath": "/products/wireless-headphones",
"attributionWindowHours": 168,
"utmParameters": {
"source": "instagram",
"medium": "social",
"campaign": "product-launch",
"content": "product-123"
}
}

User Flow:

  1. User clicks link on Instagram
  2. Mobile user redirected to App Store/Google Play
  3. User installs app
  4. App opens and navigates to wireless headphones product page
  5. Install attributed to Instagram campaign
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/referral/john-doe",
"attributionWindowHours": 2160,
"utmParameters": {
"source": "referral",
"medium": "friend",
"campaign": "refer-a-friend",
"content": "john-doe"
}
}

Event Promotion​

{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"originalUrl": "https://example.com/events/summer-concert",
"attributionWindowHours": 720,
"expiresAt": "2024-07-31T23:59:59Z",
"utmParameters": {
"source": "facebook",
"medium": "event",
"campaign": "summer-concert-2024"
}
}

Expires: July 31, 2024 (after event ends)


Best Practices​

1. Always Provide Platform-Specific URLs​

Bad:

{
"originalUrl": "https://example.com/product"
}

Good (Core):

{
"originalUrl": "https://example.com/product",
"iosAppStoreUrl": "https://apps.apple.com/app/id123",
"androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.app"
}

Good (Cloud):

{
"webFallbackUrl": "https://example.com/product",
"iosAppStoreUrl": "https://apps.apple.com/app/id123",
"androidAppStoreUrl": "https://play.google.com/store/apps/details?id=com.app"
}

Improves attribution accuracy by 3-5x.

2. Use Descriptive Titles​

Bad:

{
"title": "Link 1"
}

Good:

{
"title": "Instagram Story - Spring Sale - Wireless Headphones"
}

Makes links easier to find and manage.

3. Set Attribution Window Based on Campaign​

Campaign TypeWindow
Flash sale24-72 hours
Product launch7-14 days
Referral program30-90 days

4. Include UTM Parameters​

Always track campaign source:

{
"utmParameters": {
"source": "instagram",
"medium": "social",
"campaign": "spring-2024"
}
}

Rate Limiting​

  • Core: Rate limiting disabled by default (configurable via environment variables)
  • Cloud: 100 requests/minute per API key

See Rate Limits for details.

Guides​