Best Practices
Follow these guidelines to build secure, performant, and maintainable integrations with the WalletWerk API.
Security
Never Commit API Keys
Never commit API keys to version control. Store API keys in environment variables or use secret management services in production.
# ✅ Good: Use environment variables export WALLETWERK_API_KEY="wk_live_abc123..." # ❌ Bad: Hardcode in source files const API_KEY = "wk_live_abc123...";
Use Descriptive Names for API Keys
Name your keys based on their purpose (e.g., "Production App", "Webhook Handler", "Development Testing"). This makes it easier to audit usage and rotate keys.
Rotate keys regularly as a security best practice. You can create new keys and revoke old ones from the dashboard.
Limit Scopes
Only grant the minimum scopes needed for each API key. Use read-only keys (campaigns:read) when possible. Separate keys for different services allow for better access control.
Available Scopes:
campaigns:read— Read campaigns and recipientscampaigns:write— Create and update campaigns and recipientspkpass:verify— Verify Apple Wallet .pkpass files
Performance
Use Pagination
Always specify limit and offset when listing resources. Don't request more data than you need — smaller responses are faster and use less bandwidth.
// ✅ Good: Use pagination
const response = await fetch(
`${BASE_URL}/campaigns?limit=20&offset=0`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
// ❌ Bad: Request all data at once
const response = await fetch(
`${BASE_URL}/campaigns?limit=1000`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
Handle Rate Limits Gracefully
Monitor the rate limit headers in responses (X-RateLimit-Remaining, X-RateLimit-Reset). Implement exponential backoff for retries when you receive a 429 response:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
Cache When Appropriate
Cache recipient and campaign data on your end when possible. Don't repeatedly fetch the same wallet card information — use webhooks instead to receive updates.
Webhooks provide real-time updates without polling. Set up webhooks to receive notifications when cards become active, removed, or updated.
Error Handling
Check Response Status Codes
- 4xx errors — Client errors that require fixing your request (bad input, missing fields, invalid auth)
- 5xx errors — Server errors that may be transient; implement retry with exponential backoff
const response = await fetch(url, options);
if (!response.ok) {
if (response.status >= 400 && response.status < 500) {
// Client error - fix your request
const error = await response.json();
console.error('Client error:', error);
} else if (response.status >= 500) {
// Server error - retry with backoff
// ... retry logic
}
}
Log Errors for Debugging
Include relevant context in error logs (endpoint, timestamp, error code). Never log API keys — they should remain secret even in logs.
Validate Input Before Sending
- Check required fields before making requests
- Validate email formats
- Ensure dates are in ISO 8601 format (
YYYY-MM-DDorYYYY-MM-DDTHH:MM:SSZ)
function validateRecipient(recipient) {
if (!recipient.email || !isValidEmail(recipient.email)) {
throw new Error('Invalid email address');
}
// ... more validation
}
Integration Tips
Use External IDs
Store your internal user/customer IDs in the externalId field when creating recipients. This makes it much easier to sync data between WalletWerk and your systems.
{
email: 'user@example.com',
name: 'John Doe',
externalId: 'user_12345' // Your internal user ID
}
Set Up Webhooks
Don't poll the API for status changes. Instead, configure webhook endpoints to receive real-time notifications when cards become active, removed, or updated. Always verify webhook signatures using your signing secret.
See the Webhooks section for implementation details.
Test Your Integration Thoroughly
- Start with test API keys and verify behavior before using production keys
- Monitor usage and errors in the dashboard
- Test the complete flow: create campaign → add recipient → invite → install → update → revoke
Use the dashboard's test webhook feature to verify your webhook endpoint before going live.
Real-time Updates
When you modify a campaign or recipient, changes automatically propagate to all active cards:
- Campaign updates (name, venue, dates) → All cards in the campaign
- Recipient updates (name, data) → That recipient's card only
For Apple Wallet, updates sync on the next wallet sync (usually within minutes). For Google Wallet, updates are immediate.
Idempotency
For bulk operations, use the X-WalletWerk-Delivery-ID header from webhooks to ensure idempotent processing. Store processed delivery IDs to prevent duplicate processing.
See the Webhooks section for more details.