New features available! Check the changelog
Subscribfy

Webhooks Advanced

Technical guide for implementing and consuming Subscribfy webhooks

Real-time event notifications for building integrations and automations.


Webhook Overview

Subscribfy sends HTTP POST requests to your endpoint when events occur. Your endpoint must:

  • Accept POST requests
  • Use HTTPS
  • Return 2xx status within 30 seconds
  • Handle duplicate events gracefully

Available Events

Subscription Events

EventDescription
subscription.createdNew subscription created
subscription.activatedSubscription became active
subscription.updatedSubscription details changed
subscription.pausedSubscription paused
subscription.resumedSubscription resumed
subscription.cancelledSubscription cancelled

Billing Events

EventDescription
billing.pendingBilling attempt scheduled
billing.successPayment successful
billing.failedPayment failed
billing.retry_scheduledRetry scheduled after failure

Member Events

EventDescription
member.createdNew member registered
member.updatedMember data updated
member.tier_changedLoyalty tier changed
member.points_changedPoints balance changed

Store Credit Events

EventDescription
store_credit.addedCredits added to balance
store_credit.deductedCredits used or removed

Payload Structure

All webhooks follow this structure:

{
  "event": "subscription.created",
  "timestamp": "2024-01-15T10:30:00Z",
  "webhook_id": "wh_abc123",
  "data": {
    // Event-specific data
  }
}

Common Fields

FieldTypeDescription
eventstringEvent type identifier
timestampISO 8601When event occurred
webhook_idstringUnique webhook delivery ID
dataobjectEvent-specific payload

Payload Examples

subscription.created

{
  "event": "subscription.created",
  "timestamp": "2024-01-15T10:30:00Z",
  "webhook_id": "wh_abc123",
  "data": {
    "subscription_id": "sub_12345",
    "shopify_subscription_id": "gid://shopify/SubscriptionContract/12345",
    "customer": {
      "id": "cust_67890",
      "shopify_id": "gid://shopify/Customer/67890",
      "email": "customer@example.com",
      "first_name": "John",
      "last_name": "Doe"
    },
    "plan": {
      "id": "plan_111",
      "name": "VIP Membership",
      "price": 29.99,
      "currency": "USD",
      "billing_frequency": "monthly"
    },
    "status": "active",
    "created_at": "2024-01-15T10:30:00Z",
    "next_billing_date": "2024-02-15T10:30:00Z"
  }
}

billing.success

{
  "event": "billing.success",
  "timestamp": "2024-02-15T10:30:00Z",
  "webhook_id": "wh_def456",
  "data": {
    "billing_attempt_id": "ba_789",
    "subscription_id": "sub_12345",
    "customer": {
      "id": "cust_67890",
      "email": "customer@example.com"
    },
    "order": {
      "id": "order_111",
      "shopify_id": "gid://shopify/Order/111",
      "order_number": "#1001",
      "total": 29.99,
      "currency": "USD"
    },
    "amount": 29.99,
    "currency": "USD",
    "payment_method": {
      "type": "credit_card",
      "last_four": "4242",
      "brand": "visa"
    },
    "next_billing_date": "2024-03-15T10:30:00Z"
  }
}

billing.failed

{
  "event": "billing.failed",
  "timestamp": "2024-02-15T10:30:00Z",
  "webhook_id": "wh_ghi789",
  "data": {
    "billing_attempt_id": "ba_790",
    "subscription_id": "sub_12345",
    "customer": {
      "id": "cust_67890",
      "email": "customer@example.com"
    },
    "amount": 29.99,
    "currency": "USD",
    "error": {
      "code": "card_declined",
      "message": "Your card was declined.",
      "decline_code": "insufficient_funds"
    },
    "retry_scheduled": true,
    "retry_date": "2024-02-18T10:30:00Z",
    "attempts_remaining": 3
  }
}

member.points_changed

{
  "event": "member.points_changed",
  "timestamp": "2024-01-20T14:00:00Z",
  "webhook_id": "wh_jkl012",
  "data": {
    "member_id": "mem_12345",
    "customer": {
      "id": "cust_67890",
      "email": "customer@example.com"
    },
    "points": {
      "previous_balance": 500,
      "new_balance": 600,
      "change": 100
    },
    "reason": "order_placed",
    "order_id": "order_222"
  }
}

Implementation Guide

Endpoint Requirements

POST https://your-domain.com/webhooks/subscribfy
Content-Type: application/json
X-Subscribfy-Signature: sha256=...

Signature Verification

Verify webhook authenticity using HMAC-SHA256:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return `sha256=${expected}` === signature;
}

// Express.js example
app.post('/webhooks/subscribfy', (req, res) => {
  const signature = req.headers['x-subscribfy-signature'];
  const isValid = verifyWebhook(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook
  processEvent(req.body);
  res.status(200).send('OK');
});

Idempotency

Webhooks may be delivered multiple times. Use webhook_id for deduplication:

const processedWebhooks = new Set();

function processEvent(webhook) {
  if (processedWebhooks.has(webhook.webhook_id)) {
    console.log('Duplicate webhook, skipping');
    return;
  }

  processedWebhooks.add(webhook.webhook_id);

  // Process the event
  switch (webhook.event) {
    case 'subscription.created':
      handleSubscriptionCreated(webhook.data);
      break;
    case 'billing.failed':
      handleBillingFailed(webhook.data);
      break;
    // ...
  }
}

Retry Policy

If your endpoint fails to respond with 2xx:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours
Final24 hours

After 6 failed attempts, the webhook is marked as failed.


Testing Webhooks

Use Test Mode

Send test webhooks from Subscribfy:

  1. Go to SettingsWebhooks
  2. Click Test next to your endpoint
  3. Select event type
  4. Click Send Test

Local Development

For local testing, use a tunnel service:

# Using ngrok
ngrok http 3000

# Your endpoint becomes
# https://abc123.ngrok.io/webhooks/subscribfy

Webhook Logs

View webhook delivery logs in Subscribfy:

  1. Go to SettingsWebhooks
  2. Click on your endpoint
  3. View Delivery History

Each log shows:

  • Event type
  • Delivery status
  • Response code
  • Response time
  • Payload (click to expand)

Best Practices


Common Use Cases

Sync to Data Warehouse

async function syncToWarehouse(webhook) {
  const { event, data } = webhook;

  if (event === 'subscription.created') {
    await warehouse.insert('subscriptions', {
      id: data.subscription_id,
      customer_email: data.customer.email,
      plan: data.plan.name,
      price: data.plan.price,
      created_at: data.created_at
    });
  }
}

Trigger Email Flows

async function triggerEmail(webhook) {
  if (webhook.event === 'billing.failed') {
    await emailService.send({
      template: 'payment_failed',
      to: webhook.data.customer.email,
      data: {
        amount: webhook.data.amount,
        retry_date: webhook.data.retry_date
      }
    });
  }
}

Update CRM

async function updateCRM(webhook) {
  if (webhook.event === 'subscription.cancelled') {
    await crm.updateContact(webhook.data.customer.email, {
      subscription_status: 'cancelled',
      cancel_reason: webhook.data.cancel_reason,
      cancelled_at: webhook.timestamp
    });
  }
}

Troubleshooting

Was this page helpful?

On this page

AI Chat

Ask a question about Subscribfy