Skip to main content

Signed Links

Using the iOS SDK? You don’t need signed links. The SDK handles authentication automatically via FeatKit.identify(). This page is for web apps and custom integrations.
Signed Links let you authenticate users from your backend without exposing credentials in client code. Use this approach when:
  • Building a web app that opens the portal
  • Creating a custom integration without the SDK
  • Supporting non-iOS platforms
  • Needing server-controlled authentication

How it works

  1. Your backend generates a signed URL with user info
  2. Your app/website opens this URL
  3. User is automatically authenticated on the portal
  4. Votes and submissions are tied to their identity

What you need

  • Project Slug — from Project Settings → General
  • Secret Key — from Project Settings → Signed Links
  • User info: ID (required), email (optional), name (optional)
Your backend creates a signed token with user data using HMAC-SHA256.

Signature structure

Payload: { user_id, email, name, timestamp, expires }
Signature: HMAC-SHA256(base64(payload), secret_key)
Final URL: https://featkit.com/p/[slug]?token=[base64payload].[signature]

Code examples

import hmac
import hashlib
import base64
import json
import time

def generate_signed_link(slug, secret_key, user_id, email=None, name=None):
    payload = {
        "user_id": user_id,
        "email": email,
        "name": name,
        "timestamp": int(time.time()),
        "expires": int(time.time()) + 86400  # 24 hours
    }
    payload_json = json.dumps(payload, separators=(',', ':'))
    payload_b64 = base64.urlsafe_b64encode(payload_json.encode()).decode()
    signature = hmac.new(secret_key.encode(), payload_b64.encode(), hashlib.sha256).hexdigest()
    return f"https://featkit.com/p/{slug}?token={payload_b64}.{signature}"

Using in a web app

// Frontend: Request signed URL from your backend
const response = await fetch('/api/featkit-link');
const { url } = await response.json();

// Open in new tab or redirect
window.open(url, '_blank');
// Backend (Express example)
app.get('/api/featkit-link', (req, res) => {
  const url = generateSignedLink(
    process.env.FEATKIT_SLUG,
    process.env.FEATKIT_SECRET,
    req.user.id,
    req.user.email,
    req.user.name
  );
  res.json({ url });
});

Token expiration

Tokens should expire. Recommended: 24 hours. Set the expires field to timestamp + 86400.
Shorter expiration times (1-4 hours) are more secure but require more frequent token generation.

Testing

Generate a test link in your dashboard: Project Settings → Signed Links → Test Link Generator This lets you verify your integration without deploying backend changes.

Security notes

Never expose your Secret Key in client code. Generate tokens on your backend only.
  • Store your Secret Key securely (environment variables, secrets manager)
  • Generate tokens server-side only
  • Use HTTPS for all requests
  • Set reasonable expiration times