API Webhooks and Automation
Integrate Alva Digital Downloads with external systems using webhooks and API automation for powerful workflows.
Webhooks Overview
Section titled “Webhooks Overview”What are webhooks:
HTTP callbacks triggered by eventsReal-time notifications to your serverPush-based (no polling needed)Enable automation and integrationsUse cases:
• Send data to CRM (Salesforce, HubSpot)• Trigger email campaigns (Mailchimp)• Update analytics (Google Analytics)• Sync with accounting (QuickBooks)• Custom business logic• Third-party integrationsSetting Up Webhooks
Section titled “Setting Up Webhooks”Create Webhook Endpoint
Section titled “Create Webhook Endpoint”Configure webhook:
Settings → Advanced → Webhooks → Add Webhook
Endpoint URL: https://yourapp.com/webhooks/alvaMethod: POSTFormat: JSONAuthentication: HMAC-SHA256 signatureTimeout: 10 secondsRetry: 3 attempts (exponential backoff)Webhook Events
Section titled “Webhook Events”Available events:
Order events:
☑ order.created - New digital order placed☑ order.paid - Payment confirmed☑ order.approved - Fraud check passed☑ order.rejected - Fraud check failed☐ order.fulfilled - Files delivered☐ order.refunded - Order refundedDownload events:
☑ download.created - Download link generated☑ download.accessed - Customer downloaded file☐ download.limit_reached - Max downloads hit☐ download.expired - Link expired☐ download.revoked - Access revokedFile events:
☐ file.uploaded - New file added☐ file.updated - File modified☐ file.deleted - File removed☐ file.mapped - File mapped to productCustomer events:
☐ customer.first_purchase - First digital purchase☐ customer.repeat_purchase - Return customerWebhook Payloads
Section titled “Webhook Payloads”Order Created Event
Section titled “Order Created Event”Example payload:
{ "event": "order.created", "timestamp": "2024-01-15T15:30:00Z", "shop": "yourshop.myshopify.com", "api_version": "2024-01", "data": { "order": { "id": "1045", "number": "1045", "created_at": "2024-01-15T15:30:00Z", "total": "49.99", "currency": "USD", "financial_status": "paid", "customer": { "id": "12345", "email": "customer@example.com", "first_name": "John", "last_name": "Smith", "phone": "+1-555-123-4567" }, "line_items": [ { "id": "67890", "product_id": "prod_123", "title": "Complete Course Bundle", "quantity": 1, "price": "49.99", "files": [ { "id": "file_abc123", "filename": "Chapter-1.pdf", "size": 2500000, "download_url": "https://..." } ] } ], "download_link": "https://yourshop.com/download?key=...", "expiry_date": "2024-03-15T15:30:00Z", "download_limit": 5 } }, "signature": "abc123def456..."}Download Accessed Event
Section titled “Download Accessed Event”Example payload:
{ "event": "download.accessed", "timestamp": "2024-01-15T16:00:00Z", "shop": "yourshop.myshopify.com", "data": { "download": { "id": "download_xyz789", "token": "abc123def456", "file": { "id": "file_abc123", "filename": "Chapter-1.pdf", "size": 2500000 }, "customer": { "email": "customer@example.com", "name": "John Smith" }, "order_number": "1045", "download_count": 1, "download_limit": 5, "downloads_remaining": 4, "ip_address": "192.168.1.100", "user_agent": "Mozilla/5.0...", "location": { "country": "US", "region": "CA", "city": "San Francisco" } } }}Webhook Security
Section titled “Webhook Security”Verifying Webhooks
Section titled “Verifying Webhooks”HMAC signature verification:
Node.js example:
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) { const hmac = crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex');
// Use timing-safe comparison return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(hmac) );}
// Express.js routeapp.post('/webhooks/alva', (req, res) => { const signature = req.headers['x-alva-hmac-sha256']; const secret = process.env.WEBHOOK_SECRET;
if (!verifyWebhook(req.body, signature, secret)) { return res.status(401).send('Invalid signature'); }
// Process webhook const event = req.body.event; const data = req.body.data;
// Handle event...
res.status(200).send('OK');});Python example:
import hmacimport hashlibimport json
def verify_webhook(payload, signature, secret): computed = hmac.new( secret.encode(), json.dumps(payload).encode(), hashlib.sha256 ).hexdigest()
return hmac.compare_digest(signature, computed)
# Flask route@app.route('/webhooks/alva', methods=['POST'])def handle_webhook(): signature = request.headers.get('X-Alva-HMAC-SHA256') secret = os.environ['WEBHOOK_SECRET']
if not verify_webhook(request.json, signature, secret): return 'Invalid signature', 401
event = request.json['event'] data = request.json['data']
# Handle event...
return 'OK', 200IP Whitelisting
Section titled “IP Whitelisting”Restrict webhook sources:
Settings → Webhooks → Security → IP Whitelist
Add Alva webhook IPs:• 192.0.2.1• 192.0.2.2• 192.0.2.3
Firewall: Allow only these IPsResult: Additional security layerWebhook Processing
Section titled “Webhook Processing”Handling Events
Section titled “Handling Events”Event processing example:
CRM integration:
async function handleOrderCreated(data) { const order = data.order; const customer = order.customer;
// Add customer to CRM await addToCRM({ email: customer.email, firstName: customer.first_name, lastName: customer.last_name, tags: ['digital-customer'], customFields: { lastPurchase: order.created_at, totalSpent: order.total, productPurchased: order.line_items[0].title } });
// Trigger email campaign await triggerCampaign({ email: customer.email, campaign: 'post-purchase-onboarding', variables: { productName: order.line_items[0].title, downloadLink: order.download_link } });
console.log(`Customer ${customer.email} added to CRM`);}Analytics tracking:
async function handleDownloadAccessed(data) { const download = data.download;
// Send to Google Analytics await trackEvent({ category: 'Downloads', action: 'File Downloaded', label: download.file.filename, value: 1, customDimensions: { cd1: download.customer.email, cd2: download.order_number, cd3: download.location.country } });
// Send to custom analytics await logDownload({ file_id: download.file.id, customer: download.customer.email, ip: download.ip_address, country: download.location.country, timestamp: new Date() });}Asynchronous Processing
Section titled “Asynchronous Processing”Handle webhooks efficiently:
Queue-based processing:
const Queue = require('bull');const webhookQueue = new Queue('webhooks');
// Receive webhookapp.post('/webhooks/alva', async (req, res) => { // Quick validation if (!verifyWebhook(req.body, req.headers['x-alva-hmac-sha256'])) { return res.status(401).send('Invalid'); }
// Add to queue (fast response) await webhookQueue.add({ event: req.body.event, data: req.body.data });
// Respond immediately res.status(200).send('Queued');});
// Process in backgroundwebhookQueue.process(async (job) => { const { event, data } = job.data;
switch (event) { case 'order.created': await handleOrderCreated(data); break; case 'download.accessed': await handleDownloadAccessed(data); break; // ... other events }});API Integration
Section titled “API Integration”REST API
Section titled “REST API”API authentication:
Authorization: Bearer {api_key}Content-Type: application/jsonGet API key:
Settings → Advanced → API Access[Generate API Key]
Key: ak_live_abc123def456...Store: Environment variable (keep secret)API Endpoints
Section titled “API Endpoints”Common endpoints:
List files:
GET /api/filesAuthorization: Bearer {api_key}
Query parameters:?page=1&limit=50?tags=course,premium?unmapped=true
Response:{ "files": [...], "total": 247, "page": 1, "pages": 5}Get file details:
GET /api/files/{file_id}Authorization: Bearer {api_key}
Response:{ "id": "file_abc123", "filename": "Chapter-1.pdf", "size": 2500000, "upload_date": "2024-01-15", "products": ["prod_123"], "tags": ["course", "chapter1"], "downloads": 145}Create download link:
POST /api/downloads/generateAuthorization: Bearer {api_key}Content-Type: application/json
{ "file_id": "file_abc123", "customer_email": "customer@example.com", "expiry_days": 7, "download_limit": 3}
Response:{ "download_url": "https://yourshop.com/download?key=...", "token": "abc123def456", "expires_at": "2024-01-22T15:30:00Z", "download_limit": 3}Rate Limiting
Section titled “Rate Limiting”API rate limits:
Limits:• 1000 requests/hour• 100 requests/minute burst
Headers returned:X-RateLimit-Limit: 1000X-RateLimit-Remaining: 847X-RateLimit-Reset: 1610000000
429 response: Too Many RequestsRetry-After: 3600 (seconds)Handle rate limits:
async function apiRequest(endpoint, options) { const response = await fetch(endpoint, options);
if (response.status === 429) { const retryAfter = response.headers.get('Retry-After'); console.log(`Rate limited. Retry after ${retryAfter}s`);
await sleep(retryAfter * 1000); return apiRequest(endpoint, options); // Retry }
return response.json();}Automation Workflows
Section titled “Automation Workflows”Email Campaign Integration
Section titled “Email Campaign Integration”Mailchimp automation:
// Add customer to Mailchimp after purchaseasync function syncToMailchimp(orderData) { const customer = orderData.order.customer;
const response = await fetch( 'https://us1.api.mailchimp.com/3.0/lists/{list_id}/members', { method: 'POST', headers: { 'Authorization': 'Bearer ' + process.env.MAILCHIMP_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ email_address: customer.email, status: 'subscribed', merge_fields: { FNAME: customer.first_name, LNAME: customer.last_name }, tags: ['digital-customer', 'alva-downloads'] }) } );
console.log('Added to Mailchimp:', customer.email);}Zapier Integration
Section titled “Zapier Integration”Connect to 5000+ apps:
Setup Zapier webhook:
1. Create Zap: Webhooks by Zapier (Trigger)2. Get webhook URL: https://hooks.zapier.com/hooks/catch/...3. Alva → Webhooks → Add webhook → Paste Zapier URL4. Test webhook5. Zapier: Add action (e.g., Google Sheets, Slack, etc.)6. Enable ZapExample Zaps:
• New order → Add row to Google Sheets• Download accessed → Send Slack notification• Order approved → Create Trello card• File uploaded → Post to DiscordMake (Integromat) Integration
Section titled “Make (Integromat) Integration”Visual automation:
Webhook trigger:
1. Make → Create scenario2. Add webhook module3. Copy webhook URL4. Alva → Add webhook → Paste URL5. Add action modules (any apps)6. Activate scenarioExample scenarios:
• Order → Update Airtable → Send SMS via Twilio• Download → Log to database → Send analytics to Slack• New customer → Add to CRM → Assign to sales teamCustom Integrations
Section titled “Custom Integrations”Build Custom Integration
Section titled “Build Custom Integration”Full integration example:
Sync orders to custom database:
const express = require('express');const app = express();const db = require('./database');
app.use(express.json());
app.post('/webhooks/alva', async (req, res) => { // Verify webhook if (!verifyWebhook(req.body, req.headers['x-alva-hmac-sha256'])) { return res.status(401).send('Invalid'); }
const { event, data } = req.body;
try { switch (event) { case 'order.created': await handleOrderCreated(data); break; case 'download.accessed': await logDownload(data); break; }
res.status(200).send('OK'); } catch (error) { console.error('Webhook error:', error); res.status(500).send('Error processing webhook'); }});
async function handleOrderCreated(data) { const order = data.order;
// Save to database await db.orders.insert({ order_id: order.id, order_number: order.number, customer_email: order.customer.email, total: order.total, currency: order.currency, created_at: order.created_at, files: JSON.stringify(order.line_items.map(item => item.files)), download_link: order.download_link });
// Send custom notification await sendNotification({ type: 'new_order', order_number: order.number, customer: order.customer.email, total: order.total });}
async function logDownload(data) { const download = data.download;
await db.downloads.insert({ file_id: download.file.id, filename: download.file.filename, customer_email: download.customer.email, order_number: download.order_number, ip_address: download.ip_address, country: download.location.country, timestamp: new Date(), download_count: download.download_count });}
app.listen(3000);Monitoring Webhooks
Section titled “Monitoring Webhooks”Webhook Logs
Section titled “Webhook Logs”View webhook delivery:
Settings → Webhooks → Logs
Columns:• Timestamp• Event type• Endpoint URL• HTTP status• Response time• Retry count• Payload (expandable)Filter logs:
☐ Failed deliveries only☐ Specific event type☐ Last 24 hours☐ Specific endpointRetry Logic
Section titled “Retry Logic”Failed webhook handling:
Automatic retries:
Retry strategy:Attempt 1: ImmediateAttempt 2: After 5 secondsAttempt 3: After 25 seconds (5^2)Attempt 4: After 125 seconds (5^3)
Max retries: 3Timeout: 10 seconds per attempt
Final failure: Logged, notification sentManual retry:
Webhook logs → Failed delivery → [Retry]Resends webhook to endpointTroubleshooting
Section titled “Troubleshooting”Webhooks Not Received
Section titled “Webhooks Not Received”Debug checklist:
☐ Endpoint URL correct and accessible☐ Server accepts POST requests☐ Firewall allows webhooks☐ SSL certificate valid (HTTPS required)☐ Endpoint responds within 10 seconds☐ Returns 200 OK status☐ Check webhook logs for errorsTest endpoint:
Settings → Webhooks → Test WebhookSends test payload to endpointView response and errorsSignature Verification Failing
Section titled “Signature Verification Failing”Fix verification:
1. Use correct webhook secret (from settings)2. Hash entire raw request body (not parsed JSON)3. Use timing-safe comparison4. Check HMAC algorithm (SHA-256)5. Ensure secret not expired/regeneratedBest Practices
Section titled “Best Practices”1. Respond Quickly
Section titled “1. Respond Quickly”Fast response:
✓ Respond 200 OK immediately✓ Process in background queue✓ Don't wait for long operations✓ Timeout: < 1 second ideal
❌ Don't process synchronously❌ Don't wait for external API calls❌ Don't perform heavy computations2. Handle Duplicates
Section titled “2. Handle Duplicates”Idempotent processing:
Use unique webhook ID or order IDCheck if already processedSkip if duplicate
Example:const processed = await db.webhooks.find(webhook_id);if (processed) { return; // Already handled}
// Process webhook...await db.webhooks.insert({ webhook_id, processed: true });3. Log Everything
Section titled “3. Log Everything”Comprehensive logging:
✓ All webhook receipts✓ Processing results✓ Errors and exceptions✓ Retry attempts✓ Timestamp all events
Helps troubleshootingAudit trail4. Monitor and Alert
Section titled “4. Monitor and Alert”Proactive monitoring:
✓ Alert on failed webhooks✓ Monitor processing time✓ Track error rates✓ Set up health checks✓ Review logs regularly5. Test Thoroughly
Section titled “5. Test Thoroughly”Testing approach:
✓ Test each event type✓ Test with real payloads✓ Test error scenarios✓ Test retry logic✓ Load test with high volumeNext Steps
Section titled “Next Steps”- File Watermarking - Content protection
- Custom Domain Setup - Branded experience
- Advanced Analytics - Data insights
- Advanced Settings and API - API reference