Skip to main content

Projects

Cloud Feature

Projects are a Cloud-only feature. Self-hosted Core deployments do not include project-based link organization. See Cloud vs Self-Hosted for details.

Organize links into projects for better management and campaign tracking.

Overview​

Projects are folders that help you organize links within your organization. They're perfect for:

  • Grouping links by campaign
  • Separating client work
  • Organizing by department or team
  • Tracking project-specific analytics

Key Benefits:

  • Organization - Group related links together
  • Analytics - View aggregated metrics per project
  • Filtering - Quickly find links by project
  • Collaboration - Share project context with team

Creating Projects​

Via Dashboard​

  1. Go to Projects in the sidebar
  2. Click "New Project"
  3. Enter project name
  4. (Optional) Add description
  5. Click "Create"

Via API​

curl -X POST https://api.linkforty.com/api/projects \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Spring Sale 2024",
"description": "Instagram and email marketing campaign for Q1 spring sale"
}'

Response:

{
"id": "proj_abc123",
"user_id": "user_456",
"organization_id": "org_789",
"name": "Spring Sale 2024",
"description": "Instagram and email marketing campaign for Q1 spring sale",
"created_at": "2024-03-15T10:30:00Z",
"updated_at": "2024-03-15T10:30:00Z"
}

Requires: Member, Admin, or Owner role

Managing Projects​

Viewing All Projects​

Dashboard:

  • Click "Projects" in sidebar
  • See all projects with link counts

API:

curl https://api.linkforty.com/api/projects \
-H "Authorization: Bearer $LINKFORTY_API_KEY"

Response:

[
{
"id": "proj_123",
"name": "Spring Sale 2024",
"description": "Q1 spring sale campaign",
"created_at": "2024-03-15T10:30:00Z",
"updated_at": "2024-03-15T10:30:00Z"
},
{
"id": "proj_456",
"name": "Product Launch",
"description": "New product launch links",
"created_at": "2024-02-01T14:00:00Z",
"updated_at": "2024-02-10T09:15:00Z"
}
]

Getting Project Details​

API:

curl https://api.linkforty.com/api/projects/proj_abc123 \
-H "Authorization: Bearer $LINKFORTY_API_KEY"

Response:

{
"id": "proj_abc123",
"user_id": "user_456",
"organization_id": "org_789",
"name": "Spring Sale 2024",
"description": "Instagram and email marketing campaign for Q1 spring sale",
"created_at": "2024-03-15T10:30:00Z",
"updated_at": "2024-03-15T10:30:00Z"
}

Updating Projects​

Dashboard:

  1. Go to Projects
  2. Click project name
  3. Click "Edit"
  4. Update name or description
  5. Click "Save"

API:

curl -X PUT https://api.linkforty.com/api/projects/proj_abc123 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Spring Sale 2024 - Extended",
"description": "Extended through April due to success"
}'

Requires: Member, Admin, or Owner role

Deleting Projects​

Warning: Deleting a project does NOT delete the links inside. Links are unassigned and remain in your organization.

Dashboard:

  1. Go to Projects
  2. Click project
  3. Click "Settings" → "Delete Project"
  4. Confirm deletion

API:

curl -X DELETE https://api.linkforty.com/api/projects/proj_abc123 \
-H "Authorization: Bearer $LINKFORTY_API_KEY"

Response:

{
"success": true
}

Requires: Admin or Owner role (for deletion)

Dashboard:

  1. Click "Create Link"
  2. Select project from dropdown
  3. Fill in link details
  4. Click "Create"

API:

curl -X POST https://api.linkforty.com/api/links \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"originalUrl": "https://shop.example.com/spring-sale",
"projectId": "proj_abc123",
"title": "Instagram Story - Spring Sale"
}'

Dashboard:

  1. Go to Links
  2. Click link to edit
  3. Select project from dropdown
  4. Click "Save"

API:

curl -X PUT https://api.linkforty.com/api/links/link_xyz789 \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectId": "proj_abc123"
}'

Dashboard:

  1. Go to Links
  2. Use project filter dropdown
  3. Select project

API:

curl "https://api.linkforty.com/api/links?projectId=proj_abc123" \
-H "Authorization: Bearer $LINKFORTY_API_KEY"

Project Analytics​

View aggregated analytics for all links in a project.

Via Dashboard​

  1. Go to Projects
  2. Click project name
  3. View analytics tab

Via API​

curl "https://api.linkforty.com/api/analytics/projects/proj_abc123?days=30" \
-H "Authorization: Bearer $LINKFORTY_API_KEY"

Response:

{
"totalClicks": 5432,
"uniqueClicks": 3821,
"clicksByDate": [
{
"date": "2024-03-15",
"clicks": 245
},
{
"date": "2024-03-14",
"clicks": 198
}
],
"clicksByCountry": [
{
"country": "US",
"clicks": 2341
},
{
"country": "CA",
"clicks": 987
}
],
"clicksByDevice": [
{
"device": "mobile",
"clicks": 3876
},
{
"device": "desktop",
"clicks": 1234
},
{
"device": "tablet",
"clicks": 322
}
],
"clicksByPlatform": [
{
"platform": "iOS",
"clicks": 2145
},
{
"platform": "Android",
"clicks": 1731
},
{
"platform": "Windows",
"clicks": 1556
}
]
}

Query Parameters:

  • days - Number of days to include (default: 30, max: 365)

Common Workflows​

1. Campaign Organization​

Setup:

async function setupCampaignProjects() {
const campaigns = [
{ name: 'Q1 Email Campaign', description: 'Weekly newsletter links' },
{ name: 'Instagram Ads - March', description: 'Paid social media ads' },
{ name: 'Product Launch', description: 'New product announcement' },
];

for (const campaign of campaigns) {
await fetch('https://api.linkforty.com/api/projects', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(campaign),
});
}
}

2. Agency Client Organization​

Pattern:

Organization: Marketing Agency
├── Project: Client - Nike
├── Project: Client - Adidas
├── Project: Client - Under Armour
└── Project: Internal - Agency Website

Code:

async function createClientProject(clientName: string) {
const response = await fetch('https://api.linkforty.com/api/projects', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: `Client - ${clientName}`,
description: `All links and campaigns for ${clientName}`,
}),
});

return response.json();
}

3. Department Organization​

Pattern:

Organization: Acme Corp
├── Project: Marketing Team
├── Project: Sales Team
├── Project: Product Team
└── Project: Customer Success

4. Time-Based Projects​

Pattern:

Organization: E-commerce Store
├── Project: January 2024
├── Project: February 2024
├── Project: March 2024
└── Project: Q1 Summary

TypeScript Examples​

interface Project {
id: string;
name: string;
description?: string;
created_at: string;
updated_at: string;
}

interface Link {
id: string;
shortCode: string;
originalUrl: string;
projectId?: string;
title?: string;
}

async function createProjectWithLinks(
projectName: string,
links: Array<{ url: string; title: string }>
): Promise<{ project: Project; links: Link[] }> {
// Create project
const projectResponse = await fetch('https://api.linkforty.com/api/projects', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: projectName }),
});

const project = await projectResponse.json();

// Create links assigned to project
const createdLinks = [];
for (const link of links) {
const linkResponse = await fetch('https://api.linkforty.com/api/links', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
originalUrl: link.url,
title: link.title,
projectId: project.id,
}),
});

createdLinks.push(await linkResponse.json());
}

return { project, links: createdLinks };
}

// Usage
const campaign = await createProjectWithLinks('Spring Sale', [
{ url: 'https://shop.com/sale', title: 'Instagram Story 1' },
{ url: 'https://shop.com/sale', title: 'Email Newsletter' },
{ url: 'https://shop.com/sale', title: 'Facebook Ad' },
]);

console.log(`Created project: ${campaign.project.name}`);
console.log(`Created ${campaign.links.length} links`);

2. Compare Project Performance​

async function compareProjectPerformance(
projectIds: string[],
days: number = 30
) {
const results = await Promise.all(
projectIds.map(async id => {
const analyticsResponse = await fetch(
`https://api.linkforty.com/api/analytics/projects/${id}?days=${days}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const analytics = await analyticsResponse.json();

const projectResponse = await fetch(
`https://api.linkforty.com/api/projects/${id}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const project = await projectResponse.json();

return {
projectId: id,
projectName: project.name,
totalClicks: analytics.totalClicks,
uniqueClicks: analytics.uniqueClicks,
clickThroughRate: (
(analytics.uniqueClicks / analytics.totalClicks) * 100
).toFixed(1) + '%',
};
})
);

// Sort by total clicks
return results.sort((a, b) => b.totalClicks - a.totalClicks);
}

// Usage
const comparison = await compareProjectPerformance([
'proj_123',
'proj_456',
'proj_789',
]);

console.table(comparison);

3. Archive Old Projects​

async function archiveOldProjects(monthsOld: number = 6) {
const response = await fetch('https://api.linkforty.com/api/projects', {
headers: { 'Authorization': `Bearer ${API_KEY}` },
});

const projects: Project[] = await response.json();

const cutoffDate = new Date();
cutoffDate.setMonth(cutoffDate.getMonth() - monthsOld);

const oldProjects = projects.filter(project => {
const createdAt = new Date(project.created_at);
return createdAt < cutoffDate;
});

for (const project of oldProjects) {
// Check if project has any links
const linksResponse = await fetch(
`https://api.linkforty.com/api/links?projectId=${project.id}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const links = await linksResponse.json();

if (links.length === 0) {
// Delete empty old projects
await fetch(`https://api.linkforty.com/api/projects/${project.id}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${API_KEY}` },
});

console.log(`Deleted empty project: ${project.name}`);
} else {
// Rename to mark as archived
await fetch(`https://api.linkforty.com/api/projects/${project.id}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: `[ARCHIVED] ${project.name}`,
}),
});

console.log(`Archived project: ${project.name}`);
}
}
}

4. Generate Project Report​

async function generateProjectReport(projectId: string) {
// Get project details
const projectResponse = await fetch(
`https://api.linkforty.com/api/projects/${projectId}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const project = await projectResponse.json();

// Get project analytics
const analyticsResponse = await fetch(
`https://api.linkforty.com/api/analytics/projects/${projectId}?days=30`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const analytics = await analyticsResponse.json();

// Get all links in project
const linksResponse = await fetch(
`https://api.linkforty.com/api/links?projectId=${projectId}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const links = await linksResponse.json();

return {
project: {
name: project.name,
description: project.description,
created: new Date(project.created_at).toLocaleDateString(),
},
summary: {
totalLinks: links.length,
totalClicks: analytics.totalClicks,
uniqueClicks: analytics.uniqueClicks,
avgClicksPerLink: (analytics.totalClicks / links.length).toFixed(1),
avgClicksPerDay: (analytics.totalClicks / 30).toFixed(1),
},
topCountries: analytics.clicksByCountry.slice(0, 5),
deviceBreakdown: analytics.clicksByDevice,
platformBreakdown: analytics.clicksByPlatform,
};
}

// Usage
const report = await generateProjectReport('proj_abc123');
console.log(JSON.stringify(report, null, 2));
async function moveLinksToProject(
linkIds: string[],
projectId: string
): Promise<{ success: number; failed: number }> {
let success = 0;
let failed = 0;

for (const linkId of linkIds) {
try {
await fetch(`https://api.linkforty.com/api/links/${linkId}`, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ projectId }),
});

success++;
} catch (error) {
console.error(`Failed to move link ${linkId}:`, error);
failed++;
}
}

return { success, failed };
}

// Usage
const result = await moveLinksToProject(
['link_1', 'link_2', 'link_3'],
'proj_abc123'
);

console.log(`Moved ${result.success} links`);
console.log(`Failed ${result.failed} links`);

Best Practices​

1. Clear Naming Conventions​

Good:

  • "Q1 2024 - Email Marketing"
  • "Client: Nike - Spring Campaign"
  • "Product Launch - New Widget"
  • "Instagram Ads - March 2024"

Bad:

  • "Project 1"
  • "Test"
  • "Links"
  • "Stuff"

2. Use Descriptions​

const project = {
name: 'Spring Sale 2024',
description: 'Instagram stories and email newsletter links for Q1 spring sale promotion. Target: 10K clicks.'
};

3. Regular Cleanup​

// Monthly cleanup
async function monthlyProjectCleanup() {
// Archive old projects
await archiveOldProjects(6);

// Delete empty archived projects older than 1 year
await archiveOldProjects(12);
}

4. Consistent Organization Scheme​

Pick one pattern and stick to it:

Option A - By Time:

- Q1 2024
- Q2 2024
- Q3 2024

Option B - By Campaign:

- Spring Sale
- Summer Launch
- Black Friday

Option C - By Client (Agencies):

- Client: Nike
- Client: Adidas
- Client: Puma

Option D - By Channel:

- Email Marketing
- Instagram Ads
- LinkedIn Posts

5. Don't Over-Organize​

When NOT to use projects:

  • You have less than 10 links
  • All links are for one campaign
  • You're a solo user with simple needs

When to use projects:

  • Managing 50+ links
  • Multiple campaigns or clients
  • Team collaboration
  • Need separate analytics per campaign

Troubleshooting​

"Project not found"​

Cause: Project ID doesn't exist or not in your organization

Fix:

  1. List all projects: GET /api/projects
  2. Verify project ID
  3. Check organization context

Can't Delete Project​

Cause: Insufficient permissions

Fix:

  1. Check your role (Admin or Owner required for deletion)
  2. Ask admin to delete project
  3. Note: Members can create but not delete projects

Cause: Links not assigned to project

Fix:

  1. Edit link
  2. Select project from dropdown
  3. Save changes

Project Analytics Empty​

Possible causes:

  • No links in project
  • Links have no clicks yet
  • Filtering by too short time period

Fix:

  1. Verify links are assigned: GET /api/links?projectId=proj_123
  2. Check link analytics individually
  3. Increase days parameter

Next Steps​

  1. Create your first project
  2. Assign existing links to projects
  3. Set up a naming convention
  4. Review project analytics regularly
  5. Archive old projects quarterly