Skip to main content

Authentication

LinkForty Core and LinkForty Cloud use different authentication mechanisms. This page covers both approaches.

Core vs Cloud

LinkForty Core (self-hosted) uses a simple userId query parameter for identifying users. There is no built-in authentication layer — you are expected to handle auth in your own application and pass the userId to Core.

LinkForty Cloud uses API keys (Bearer tokens) for all API requests, with organization-scoped access control.


Core Authentication (Self-Hosted)

Core does not include an authentication middleware. Instead, each API request requires a userId query parameter (for GET, PUT, DELETE) or a userId field in the request body (for POST).

Your application is responsible for authenticating users and passing the correct userId to Core.

Base URL

https://your-domain.com

How It Works

For GET, PUT, DELETE requests — pass userId as a query parameter:

curl https://your-domain.com/api/links?userId=550e8400-e29b-41d4-a716-446655440000

For POST requests — include userId in the request body:

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"
}'

Integration Example

Since Core has no built-in auth, you typically place it behind your own API gateway or backend that handles user authentication:

// Your backend proxies requests to Core with the authenticated userId
app.post('/api/links', authenticateUser, async (req, res) => {
const response = await fetch('http://localhost:3000/api/links', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: req.user.id, // From your auth layer
originalUrl: req.body.originalUrl,
title: req.body.title
})
});

const link = await response.json();
res.json(link);
});

Security Considerations

Since Core does not enforce authentication, you should:

  • Never expose Core directly to the public internet without an auth layer in front of it
  • Use a reverse proxy (Nginx, Caddy) that forwards only authenticated requests
  • Restrict network access to Core's API port (firewall rules)
  • Use the CORS_ORIGIN environment variable to restrict which origins can call the API

Cloud Authentication

LinkForty Cloud uses API keys for authentication. All API requests must include a valid API key in the Authorization header.

Base URL

https://api.linkforty.com

Authentication Method

Header Format:

Authorization: Bearer YOUR_API_KEY_HERE

What API Keys Can Access

API keys are scoped to LinkForty's programmatic resources:

ResourceEndpoints
Links/api/links
Analytics & events/api/analytics/*
Projects/api/projects
Templates/api/templates
SDK & pixel ingestion/api/sdk/v1/*, /api/pixel/v1/* (authenticated by app token / site key, not the API key)

Account, workspace, team, and billing management is performed in the LinkForty dashboard and is not part of the API. This keeps API keys narrowly scoped to link, analytics, template, and project automation.

Getting Your API Key

Via Dashboard:

  1. Log in to your LinkForty dashboard
  2. Navigate to Settings, then API Keys
  3. Click Create API Key
  4. Enter a descriptive name (e.g., "Production Server", "CI/CD Pipeline")
  5. Click Generate
  6. Copy and save the key immediately — it will not be shown again

API keys are created and managed in the dashboard (Settings → API Keys); the key-management endpoints require a logged-in session and cannot be called with an API key. When you generate a key, the response includes the secret once:

{
"id": "key_abc123xyz",
"name": "New Production Key",
"key": "dl_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"createdAt": "2024-01-15T10:30:00Z",
"lastUsedAt": null
}

Save the key value immediately. It cannot be retrieved later.

API Key Format

LinkForty API keys are a dl_ prefix followed by 64 hexadecimal characters:

dl_{64_hex_characters}

Example:

dl_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2

Making Authenticated Requests

Core (Self-Hosted)

# List links for a user
curl https://your-domain.com/api/links?userId=550e8400-e29b-41d4-a716-446655440000

# Create a link
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 Link"
}'

Cloud (cURL)

curl -X GET https://api.linkforty.com/api/links \
-H "Authorization: Bearer dl_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"

Cloud (JavaScript)

const API_KEY = process.env.LINKFORTY_API_KEY;

const response = await fetch('https://api.linkforty.com/api/links', {
method: 'GET',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
}
});

const links = await response.json();

Cloud (Node.js with axios)

const axios = require('axios');

const api = axios.create({
baseURL: 'https://api.linkforty.com',
headers: {
'Authorization': `Bearer ${process.env.LINKFORTY_API_KEY}`,
'Content-Type': 'application/json'
}
});

// Get all links
const { data: links } = await api.get('/api/links');

// Create a link
const { data: newLink } = await api.post('/api/links', {
templateId: 'template_123',
originalUrl: 'https://example.com/product'
});

Cloud (Python)

import requests
import os

API_KEY = os.environ.get('LINKFORTY_API_KEY')
BASE_URL = 'https://api.linkforty.com'

headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}

# Get all links
response = requests.get(f'{BASE_URL}/api/links', headers=headers)
links = response.json()

# Create a link
response = requests.post(
f'{BASE_URL}/api/links',
headers=headers,
json={
'templateId': 'template_123',
'originalUrl': 'https://example.com/product'
}
)
new_link = response.json()

Cloud (Go)

package main

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)

const baseURL = "https://api.linkforty.com"

func main() {
apiKey := os.Getenv("LINKFORTY_API_KEY")

client := &http.Client{}

// Get all links
req, _ := http.NewRequest("GET", baseURL+"/api/links", nil)
req.Header.Set("Authorization", "Bearer "+apiKey)

resp, _ := client.Do(req)
defer resp.Body.Close()

var links []map[string]interface{}
json.NewDecoder(resp.Body).Decode(&links)
fmt.Println(links)
}

Cloud (Ruby)

require 'net/http'
require 'json'

API_KEY = ENV['LINKFORTY_API_KEY']
BASE_URL = 'https://api.linkforty.com'

# Get all links
uri = URI("#{BASE_URL}/api/links")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{API_KEY}"
request['Content-Type'] = 'application/json'

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end

links = JSON.parse(response.body)
puts links

Security Best Practices

1. Never Hardcode API Keys

Bad:

const API_KEY = 'dl_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2';

Good:

const API_KEY = process.env.LINKFORTY_API_KEY;

2. Use Environment Variables

.env file:

LINKFORTY_API_KEY=dl_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2

Load in your app:

// Node.js (with dotenv)
require('dotenv').config();
const API_KEY = process.env.LINKFORTY_API_KEY;

// Python (with python-dotenv)
from dotenv import load_dotenv
import os
load_dotenv()
API_KEY = os.environ.get('LINKFORTY_API_KEY')

Add to .gitignore:

.env
.env.local

3. Use Separate Keys for Different Environments

Create different API keys for:

  • Production server
  • Staging server
  • Development/local
  • CI/CD pipelines
  • Each team member

4. Rotate Keys Regularly

Recommended: Rotate API keys every 90 days.

Steps:

  1. Create new API key
  2. Update environment variables in all systems
  3. Test new key works
  4. Delete old key

5. Restrict Key Permissions (Coming Soon)

Future feature: Scope keys to specific actions.

{
"name": "Read-Only Analytics Key",
"permissions": ["analytics:read"],
"rateLimit": 100
}

6. Monitor Key Usage

Check when keys were last used in the dashboard under Settings → API Keys — the "Last Used" column shows recent activity, and you can delete unused keys there. The dashboard shows each key with its prefix and last-used timestamp:

{
"apiKeys": [
{
"id": "key_abc123",
"name": "Production API Key",
"keyPrefix": "dl_a1b2c3d4****",
"createdAt": "2024-01-15T10:30:00Z",
"lastUsedAt": "2024-01-20T14:25:00Z"
}
]
}

Error Responses

Missing Authentication (Cloud)

Request:

curl -X GET https://api.linkforty.com/api/links
# No Authorization header

Response: 401 Unauthorized

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

Invalid API Key (Cloud)

Response: 401 Unauthorized

{
"error": "Unauthorized",
"message": "Invalid API key",
"statusCode": 401
}

Deleted or Revoked Key (Cloud)

Response: 401 Unauthorized

{
"error": "Unauthorized",
"message": "API key has been deleted or revoked",
"statusCode": 401
}

Wrong Organization (Cloud)

Response: 403 Forbidden

{
"error": "Forbidden",
"message": "You do not have access to this resource",
"statusCode": 403
}

Missing userId (Core)

Request:

curl https://your-domain.com/api/links
# No userId query parameter

Response: 400 Bad Request

{
"error": "Bad Request",
"message": "userId query parameter is required",
"statusCode": 400
}

Managing API Keys (Cloud Only)

API keys are managed in the dashboard under Settings → API Keys (these endpoints require a logged-in session and cannot be called with an API key):

  • Create — click Create API Key, name it, and copy the secret once (it can't be retrieved later).
  • List — view all keys with their prefix and last-used time.
  • Rename — update a key's descriptive name.
  • Delete — revoke a key. Deletion is immediate; all requests using that key fail instantly.

Testing Your Setup

Core (Self-Hosted)

# Health check (no auth required)
curl https://your-domain.com/health

# Create a test link
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",
"title": "Test Link"
}'

# List links
curl "https://your-domain.com/api/links?userId=550e8400-e29b-41d4-a716-446655440000"

Cloud

# Health check
curl -X GET https://api.linkforty.com/health

# Test API key (lists your links — returns 200 with a valid key)
curl -X GET https://api.linkforty.com/api/links \
-H "Authorization: Bearer $LINKFORTY_API_KEY"

Rate Limiting

See Rate Limits for details on API rate limiting.

Summary:

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

Troubleshooting

"Missing or invalid API key" (Cloud)

Cause: Authorization header not included or malformed.

Solutions:

  1. Verify header format: Authorization: Bearer YOUR_API_KEY
  2. Check for typos in "Authorization" or "Bearer"
  3. Ensure API key is complete (starts with dl_)

"API key has been deleted or revoked" (Cloud)

Cause: The API key was deleted from the dashboard.

Solution:

  1. Create new API key in dashboard
  2. Update environment variables
  3. Restart application

"You do not have access to this resource" (Cloud)

Cause: API key belongs to different organization than resource.

Solution:

  1. Verify you're using correct API key
  2. Check resource ID belongs to your organization
  3. Ensure you have proper role (Owner/Admin)

"userId query parameter is required" (Core)

Cause: Missing userId in query string or request body.

Solution:

  1. For GET/PUT/DELETE: add ?userId=YOUR_UUID to the URL
  2. For POST: include userId in the JSON request body
  3. Ensure userId is a valid UUID

Requests Work Locally But Fail in Production

Cause: Environment variable not set in production.

Solution:

  1. Verify LINKFORTY_API_KEY is set in production environment (Cloud)
  2. Verify Core is accessible from your application server (self-hosted)
  3. Check firewall rules and CORS configuration

Next Steps