Skip to main content

Update Link

Modify an existing link's properties including URLs, UTM parameters, targeting rules, and activation status.

Endpoint

PUT /api/links/:id

Authentication

  • Core (Self-Hosted): Pass userId as a query parameter. See Authentication.
  • Cloud: Requires a valid API key in the Authorization header. See Authentication.

Path Parameters

ParameterTypeRequiredDescription
idstring (UUID)YesLink ID to update

Query Parameters

ParameterTypeRequiredDescription
userIdstring (UUID)Core onlyRequired for Core to verify ownership

Request Body

All fields are optional - only include the fields you want to update.

FieldTypeDescription
originalUrlstring (URL)Default destination URL
titlestringLink title/name
descriptionstringLink description
iosUrlstring (URL)iOS-specific redirect URL
androidUrlstring (URL)Android-specific redirect URL
webFallbackUrlstring (URL)Web fallback URL
utmParametersobjectUTM tracking parameters
utmParameters.sourcestringUTM source
utmParameters.mediumstringUTM medium
utmParameters.campaignstringUTM campaign
utmParameters.termstringUTM term
utmParameters.contentstringUTM content
targetingRulesobjectGeographic/device targeting
targetingRules.countriesstring[]ISO country codes (e.g., ["US", "CA"])
targetingRules.devicesstring[]Device types: "ios", "android", "web"
targetingRules.languagesstring[]Language codes (e.g., ["en", "es"])
attributionWindowHoursnumberHours for install attribution (1-2160)
expiresAtstring (ISO 8601)Expiration timestamp
isActivebooleanWhether link is active
templateIdstring (UUID)Link template to use
projectIdstring (UUID)Project ID

Note: You cannot update the shortCode - it's immutable after creation.

Response

Returns the updated link object:

{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"shortCode": "abc123",
"originalUrl": "https://example.com/products/new-headphones",
"title": "Updated: Wireless Headphones",
"iosUrl": "https://apps.apple.com/app/id123456789",
"androidUrl": "https://play.google.com/store/apps/details?id=com.app",
"utmParameters": {
"source": "facebook",
"medium": "cpc",
"campaign": "summer-sale-2024"
},
"attributionWindowHours": 336,
"isActive": true,
"updatedAt": "2024-03-15T14:30:00Z"
}

Examples

1. Update Destination URL

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"originalUrl": "https://example.com/new-product-page"
}'

Use case: Product link changed to different product.

2. Update UTM Parameters

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"utmParameters": {
"source": "twitter",
"medium": "social",
"campaign": "summer-sale-2024",
"content": "tweet-1"
}
}'

Use case: Change campaign tracking for different channel.

3. Extend Attribution Window

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"attributionWindowHours": 720
}'

Use case: Extend attribution from 7 days (168h) to 30 days (720h) for longer consideration purchases.

4. Update Platform URLs

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"iosUrl": "https://apps.apple.com/app/id999999",
"androidUrl": "https://play.google.com/store/apps/details?id=com.newapp"
}'

Use case: App store URLs changed after app relaunch.

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"isActive": false
}'

Use case: Temporarily disable link without deleting (campaign ended).

6. Add Expiration Date

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"expiresAt": "2024-12-31T23:59:59Z"
}'

Use case: Set expiration for limited-time offer.

7. Update Multiple Fields

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Black Friday Sale - Wireless Headphones",
"originalUrl": "https://example.com/black-friday/headphones",
"utmParameters": {
"campaign": "black-friday-2024",
"content": "hero-banner"
},
"expiresAt": "2024-11-30T23:59:59Z"
}'

Use case: Update link for seasonal campaign.

8. Update Targeting Rules

curl -X PUT https://api.linkforty.com/api/links/abc123-def456 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"targetingRules": {
"countries": ["US", "CA", "GB"],
"devices": ["ios", "android"]
}
}'

Use case: Expand campaign to additional countries.

TypeScript Example

interface UpdateLinkRequest {
originalUrl?: string;
title?: string;
description?: string;
iosUrl?: string;
androidUrl?: string;
webFallbackUrl?: string;
utmParameters?: {
source?: string;
medium?: string;
campaign?: string;
term?: string;
content?: string;
};
targetingRules?: {
countries?: string[];
devices?: ('ios' | 'android' | 'web')[];
languages?: string[];
};
attributionWindowHours?: number;
expiresAt?: string;
isActive?: boolean;
templateId?: string;
projectId?: string;
}

async function updateLink(linkId: string, updates: UpdateLinkRequest) {
const response = await fetch(`https://api.linkforty.com/api/links/${linkId}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(updates),
});

if (!response.ok) {
throw new Error(`Failed to update link: ${response.statusText}`);
}

return response.json();
}

// Usage
const updated = await updateLink('link_id', {
title: 'New Product Launch',
utmParameters: {
campaign: 'product-launch-2024',
},
});

console.log('Updated:', updated.shortCode);

Common Use Cases

1. Seasonal Campaign Updates

Update links when campaigns change:

// Summer → Fall transition
await updateLink(linkId, {
title: 'Fall Sale - Wireless Headphones',
utmParameters: {
campaign: 'fall-sale-2024',
},
expiresAt: '2024-11-30T23:59:59Z',
});

2. A/B Test Different Destinations

Test different landing pages:

// Variant A
await updateLink('link_a', {
originalUrl: 'https://example.com/landing-page-a',
utmParameters: { content: 'variant-a' },
});

// Variant B
await updateLink('link_b', {
originalUrl: 'https://example.com/landing-page-b',
utmParameters: { content: 'variant-b' },
});

// After 1 week, check analytics and update losing variant

3. Update App Store URLs After Release

When new app version releases:

await updateLink(linkId, {
iosUrl: 'https://apps.apple.com/app/id123456789',
androidUrl: 'https://play.google.com/store/apps/details?id=com.app.v2',
});

4. Pause Campaign

Temporarily disable without deleting:

// Pause
await updateLink(linkId, { isActive: false });

// Resume later
await updateLink(linkId, { isActive: true });

Error Responses

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

Possible causes:

  • Link ID doesn't exist
  • Link belongs to different organization

No Updates Provided

{
"statusCode": 400,
"error": "Bad Request",
"message": "No updates provided"
}

Cause: Empty request body with no fields to update.

Invalid URL

{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation error: Invalid URL format"
}

Cause: originalUrl, iosUrl, androidUrl, or webFallbackUrl is not a valid URL.

Invalid Attribution Window

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

Cause: attributionWindowHours outside valid range (1-2160 hours = 1 hour to 90 days).

Best Practices

1. Update Only What Changed

Don't send entire object - only changed fields:

Good:

await updateLink(linkId, {
title: 'New Title',
});

Bad:

// Don't re-send unchanged fields
await updateLink(linkId, {
title: 'New Title',
originalUrl: link.originalUrl, // unchanged
iosUrl: link.iosUrl, // unchanged
androidUrl: link.androidUrl, // unchanged
// etc...
});

2. Validate Before Updating

function validateUrl(url: string): boolean {
try {
new URL(url);
return true;
} catch {
return false;
}
}

async function safeUpdateLink(linkId: string, updates: UpdateLinkRequest) {
// Validate URLs before sending
if (updates.originalUrl && !validateUrl(updates.originalUrl)) {
throw new Error('Invalid originalUrl');
}

return updateLink(linkId, updates);
}

3. Handle Optimistic Updates

Update UI immediately, rollback on error:

async function optimisticUpdate(link: Link, updates: UpdateLinkRequest) {
const originalLink = { ...link };
const updatedLink = { ...link, ...updates };

try {
// Update UI immediately
setLink(updatedLink);

// Send to server
const result = await updateLink(link.id, updates);

// Sync with server response
setLink(result);
} catch (error) {
// Rollback on error
setLink(originalLink);
showError('Failed to update link');
}
}

4. Batch Updates

If updating multiple fields, send one request:

Good:

await updateLink(linkId, {
title: 'New Title',
originalUrl: 'https://example.com/new',
utmParameters: { campaign: 'new-campaign' },
});

Bad:

await updateLink(linkId, { title: 'New Title' });
await updateLink(linkId, { originalUrl: 'https://example.com/new' });
await updateLink(linkId, { utmParameters: { campaign: 'new-campaign' } });

5. Track Update History

Log updates for audit trail:

async function updateLinkWithHistory(
linkId: string,
updates: UpdateLinkRequest,
reason: string
) {
const updatedLink = await updateLink(linkId, updates);

// Log update
await logUpdate({
linkId,
updates,
reason,
timestamp: new Date(),
updatedBy: currentUser.id,
});

return updatedLink;
}

Limitations

  • Short code cannot be changed - It's immutable after creation
  • Template change - Changing templateId doesn't retroactively apply template defaults
  • Click count - Cannot be manually modified (read-only)
  • Timestamps - createdAt cannot be changed, updatedAt is automatic

Guides