Implement Appizer securely to protect your data and users.
API Key Management
Never Expose Secret Keys
Secret keys (sk_) must never be exposed in client-side code:
// ❌ DANGER: Secret key in browser
const appizer = new Appizer({
apiKey: 'sk_live_abc123...' // Exposed to users!
})
// ✅ SAFE: Use public key in browser
const appizer = new AppizerClient({
publicKey: 'pk_live_xyz789...' // Safe for client-side
})
Use Environment Variables
Store API keys in environment variables:
# .env
APPIZER_API_KEY=sk_live_your_secret_key
APPIZER_PUBLIC_KEY=pk_live_your_public_key
// Server-side
const appizer = new Appizer({
apiKey: process.env.APPIZER_API_KEY
})
// Client-side
const appizer = new AppizerClient({
publicKey: process.env.NEXT_PUBLIC_APPIZER_PUBLIC_KEY
})
Critical
Rotate Keys Regularly
Rotate API keys periodically:
- Generate New KeyCreate a new API key in your Appizer dashboard
- Update ApplicationsDeploy the new key to all applications
- Revoke Old KeyDelete the old key after confirming the new one works
Use Different Keys Per Environment
Separate keys for development, staging, and production:
# Development
APPIZER_API_KEY=sk_test_dev_abc123
# Staging
APPIZER_API_KEY=sk_test_staging_def456
# Production
APPIZER_API_KEY=sk_live_prod_ghi789
Data Privacy
PII Handling
Be careful with Personally Identifiable Information (PII):
// ❌ Avoid sending sensitive PII
appizer.identify({
userId: 'user_123',
traits: {
ssn: '123-45-6789', // Don't send
creditCard: '4111...', // Don't send
password: 'secret123' // Never send
}
})
// ✅ Hash or omit sensitive data
appizer.identify({
userId: 'user_123',
traits: {
email: 'user@example.com', // OK if needed
plan: 'enterprise', // Non-sensitive
hashedId: sha256(userId) // Hashed if needed
}
})
Data Minimization
Only track data you actually need:
// ❌ Over-collection
appizer.track({
event: 'page_view',
properties: {
fullUrl: window.location.href,
allCookies: document.cookie,
localStorage: JSON.stringify(localStorage),
userAgent: navigator.userAgent
}
})
// ✅ Minimal collection
appizer.track({
event: 'page_view',
properties: {
page: '/products',
referrer: document.referrer
}
})
User Consent
Respect user privacy preferences:
// Check consent before tracking
if (userHasConsented()) {
appizer.track({ event: 'page_view' })
}
// Provide opt-out mechanism
function optOutOfTracking() {
appizer.optOut()
localStorage.setItem('tracking_opted_out', 'true')
}
Network Security
Use HTTPS Only
Always use HTTPS for API requests:
const appizer = new Appizer({
apiKey: process.env.APPIZER_API_KEY,
baseUrl: 'https://api.appizer.com' // Always HTTPS
})
HTTP Blocked
Validate SSL Certificates
Ensure SSL certificate validation is enabled:
const appizer = new Appizer({
apiKey: process.env.APPIZER_API_KEY,
validateSSL: true // Default: true
})
Set Request Timeouts
Prevent hanging connections:
const appizer = new Appizer({
apiKey: process.env.APPIZER_API_KEY,
timeout: 5000, // 5 seconds
retries: 3
})
Input Validation
Sanitize User Input
Validate and sanitize data before tracking:
function sanitizeEventName(name: string): string {
return name
.toLowerCase()
.replace(/[^a-z0-9_]/g, '_')
.substring(0, 100)
}
function trackUserAction(action: string) {
const sanitized = sanitizeEventName(action)
appizer.track({ event: sanitized })
}
Validate Property Types
Ensure properties match expected types:
interface PurchaseProperties {
amount: number
currency: string
productId: string
}
function trackPurchase(props: PurchaseProperties) {
if (typeof props.amount !== 'number' || props.amount <= 0) {
throw new Error('Invalid amount')
}
if (!/^[A-Z]{3}$/.test(props.currency)) {
throw new Error('Invalid currency code')
}
appizer.track({
event: 'purchase_completed',
properties: props
})
}
Limit Property Size
Prevent large payloads:
const MAX_PROPERTY_SIZE = 10000 // 10KB
function trackWithSizeLimit(event: string, properties: any) {
const size = JSON.stringify(properties).length
if (size > MAX_PROPERTY_SIZE) {
console.error('Properties too large:', size)
return
}
appizer.track({ event, properties })
}
Authentication
Server-Side Authentication
Perform sensitive operations server-side:
// ❌ Client-side (exposed)
appizer.identify({
userId: 'user_123',
traits: {
internalScore: 95, // Sensitive
accountBalance: 1000 // Sensitive
}
})
// ✅ Server-side (protected)
// In your API endpoint
app.post('/api/identify-user', async (req, res) => {
const { userId } = req.body
await appizer.identify({
userId,
traits: {
internalScore: calculateScore(userId),
accountBalance: getBalance(userId)
}
})
res.json({ success: true })
})
Verify Webhook Signatures
Validate webhook authenticity:
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)
}
app.post('/webhooks/appizer', (req, res) => {
const signature = req.headers['x-appizer-signature']
const payload = JSON.stringify(req.body)
if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
// Process webhook
res.status(200).send('OK')
})
Rate Limiting
Implement Client-Side Rate Limiting
Prevent excessive API calls:
class RateLimiter {
private requests: number[] = []
private limit = 100 // requests
private window = 60000 // 1 minute
canMakeRequest(): boolean {
const now = Date.now()
this.requests = this.requests.filter(t => t > now - this.window)
if (this.requests.length >= this.limit) {
return false
}
this.requests.push(now)
return true
}
}
const limiter = new RateLimiter()
function trackEvent(event: Event) {
if (!limiter.canMakeRequest()) {
console.warn('Rate limit exceeded')
return
}
appizer.track(event)
}
Handle Rate Limit Errors
Gracefully handle 429 responses:
async function trackWithRetry(event: Event) {
try {
await appizer.track(event)
} catch (error) {
if (error.status === 429) {
const retryAfter = error.headers['retry-after'] || 60
console.log(`Rate limited. Retry after ${retryAfter}s`)
setTimeout(() => trackWithRetry(event), retryAfter * 1000)
}
}
}
Error Handling
Don't Expose Internal Errors
Sanitize error messages:
try {
await appizer.track(event)
} catch (error) {
// ❌ Exposes internal details
console.error('Track failed:', error)
// ✅ Generic message
console.error('Failed to track event')
// Log detailed error server-side only
if (process.env.NODE_ENV === 'development') {
console.error('Details:', error)
}
}
Implement Circuit Breaker
Prevent cascading failures:
class CircuitBreaker {
private failures = 0
private threshold = 5
private timeout = 60000
private state: 'closed' | 'open' | 'half-open' = 'closed'
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
throw new Error('Circuit breaker is open')
}
try {
const result = await fn()
this.onSuccess()
return result
} catch (error) {
this.onFailure()
throw error
}
}
private onSuccess() {
this.failures = 0
this.state = 'closed'
}
private onFailure() {
this.failures++
if (this.failures >= this.threshold) {
this.state = 'open'
setTimeout(() => {
this.state = 'half-open'
}, this.timeout)
}
}
}
Compliance
GDPR Compliance
Implement data subject rights:
// Right to access
async function exportUserData(userId: string) {
return await appizer.users.export(userId)
}
// Right to deletion
async function deleteUserData(userId: string) {
await appizer.users.delete(userId)
}
// Right to rectification
async function updateUserData(userId: string, traits: any) {
await appizer.identify({ userId, traits })
}
Data Retention
Set appropriate retention policies:
// Configure retention in dashboard or API
await appizer.settings.update({
dataRetention: {
events: 90, // days
users: 365 // days
}
})
Security Checklist
- API Keys✓ Use environment variables ✓ Never commit to version control ✓ Separate keys per environment ✓ Rotate regularly
- Data Privacy✓ Minimize PII collection ✓ Hash sensitive data ✓ Obtain user consent ✓ Implement opt-out
- Network✓ Use HTTPS only ✓ Validate SSL certificates ✓ Set timeouts ✓ Implement rate limiting
- Code✓ Validate input ✓ Sanitize data ✓ Handle errors gracefully ✓ Use server-side for sensitive ops