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
- Your backend generates a signed URL with user info
- Your app/website opens this URL
- User is automatically authenticated on the portal
- 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)
Generating a Signed Link
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