Bonaventure OgetoBy Bonaventure Ogeto|

Paystack Subscription Emails: Disabling and Replacing Them

Disable Paystack subscription emails by setting send_invoices: false and send_sms: false when creating or updating a plan. Then build your own email notifications triggered by Paystack webhook events (charge.success, invoice.payment_failed, subscription.create, subscription.disable). This gives you full control over branding, content, and timing.

The Problem with Default Emails

Paystack sends email notifications to customers whenever a subscription event happens: charge succeeded, charge failed, subscription created, subscription cancelled. These emails are functional but generic. They use Paystack's branding, not yours.

If you also send your own emails for these events (which you should, for a professional product), the customer receives two emails for every event: one from Paystack and one from you. This is confusing, unprofessional, and erodes trust.

The solution: disable Paystack's emails and send your own. You get full control over branding, content, language, and timing. The customer gets one clear notification per event.

This article is part of the subscriptions and recurring billing guide.

Disabling Paystack Emails

Paystack email settings live on the Plan object. When you create a plan, set send_invoices and send_sms to false:

var axios = require('axios');

async function createPlanWithoutEmails() {
  var response = await axios.post(
    'https://api.paystack.co/plan',
    {
      name: 'Pro Monthly',
      amount: 500000,
      interval: 'monthly',
      currency: 'NGN',
      send_invoices: false,
      send_sms: false,
    },
    {
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
        'Content-Type': 'application/json',
      },
    }
  );

  var plan = response.data.data;
  console.log('Plan created with emails disabled:', plan.plan_code);
  return plan;
}

For existing plans, you can update the email settings:

async function disableEmailsOnExistingPlan(planCode) {
  var response = await axios.put(
    'https://api.paystack.co/plan/' + planCode,
    {
      send_invoices: false,
      send_sms: false,
    },
    {
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
        'Content-Type': 'application/json',
      },
    }
  );

  console.log('Emails disabled for plan:', planCode);
  return response.data.data;
}

Important: These settings affect all subscriptions on this plan. If you disable emails on a plan, no customer subscribed to that plan receives Paystack notifications. Make sure your own email system is working before disabling.

Building Webhook-Driven Email Notifications

With Paystack emails disabled, you send your own based on webhook events. Here is the mapping:

app.post('/webhooks/paystack', function(req, res) {
  var event = req.body;

  switch (event.event) {
    case 'subscription.create':
      sendWelcomeEmail(event.data);
      break;

    case 'charge.success':
      // Only send receipt for subscription charges, not all charges
      if (event.data.plan && event.data.plan.plan_code) {
        sendSubscriptionReceiptEmail(event.data);
      }
      break;

    case 'invoice.payment_failed':
      sendPaymentFailedEmail(event.data);
      break;

    case 'subscription.not_renew':
      sendCancellationConfirmEmail(event.data);
      break;

    case 'subscription.disable':
      sendSubscriptionEndedEmail(event.data);
      break;
  }

  res.sendStatus(200);
});

async function sendSubscriptionReceiptEmail(chargeData) {
  var customer = chargeData.customer;
  var amount = chargeData.amount / 100; // Convert from kobo
  var currency = chargeData.currency;
  var reference = chargeData.reference;
  var planName = chargeData.plan ? chargeData.plan.name : 'Your subscription';

  await emailService.send({
    to: customer.email,
    subject: 'Payment receipt for ' + planName,
    template: 'subscription_receipt',
    data: {
      customerName: customer.first_name || 'there',
      planName: planName,
      amount: currency + ' ' + amount.toLocaleString(),
      reference: reference,
      date: new Date().toLocaleDateString(),
      manageUrl: 'https://yoursite.com/billing',
    },
  });
}

async function sendPaymentFailedEmail(invoiceData) {
  var customer = invoiceData.customer;
  var cardUpdateUrl = await generateCardUpdateUrl(customer.email);

  await emailService.send({
    to: customer.email,
    subject: 'Action needed: your payment did not go through',
    template: 'payment_failed',
    data: {
      customerName: customer.first_name || 'there',
      cardUpdateUrl: cardUpdateUrl,
    },
  });
}

Key Email Templates to Build

Build these templates to cover the full subscription lifecycle:

1. Subscription Welcome

Triggered by: subscription.create

Content: Thank the customer, confirm the plan and price, state the next billing date, link to billing settings.

2. Payment Receipt

Triggered by: charge.success (for subscription charges)

Content: Confirm the payment amount, show the card charged (last4), state the billing period covered, include the transaction reference, link to download invoice.

3. Payment Failed

Triggered by: invoice.payment_failed

Content: Notify that payment failed, show which card was attempted, include a prominent card update button, explain what happens if not resolved.

4. Upcoming Renewal

Triggered by: invoice.create (optional, some products skip this)

Content: Notify that a charge will happen soon, show the amount, link to manage subscription.

5. Cancellation Confirmed

Triggered by: subscription.not_renew

Content: Confirm the cancellation, state when access ends (end of current period), offer to resubscribe, include a feedback link.

6. Subscription Ended

Triggered by: subscription.disable

Content: Notify that the subscription has ended, explain how to reactivate, assure data is preserved.

Each email should include your logo, follow your brand colors, and link to your billing page. The tone should match your product's voice.

Avoiding Duplicate Emails

Even after disabling Paystack emails, edge cases can cause duplicates or missed emails.

Webhook deduplication

Paystack can send the same webhook event multiple times. If your email handler fires on every webhook, the customer gets multiple copies of the same receipt. Deduplicate by tracking which events you have already processed:

async function processWebhookWithDedup(event) {
  var eventId = event.data.reference || event.data.subscription_code;
  var eventType = event.event;
  var deduplicationKey = eventType + '_' + eventId;

  // Check if already processed
  var existing = await db.query(
    'SELECT id FROM processed_webhook_events WHERE dedup_key = $1',
    [deduplicationKey]
  );

  if (existing.rows.length > 0) {
    console.log('Duplicate webhook skipped: ' + deduplicationKey);
    return;
  }

  // Record before processing
  await db.query(
    'INSERT INTO processed_webhook_events (dedup_key, event_type, processed_at) VALUES ($1, $2, NOW())',
    [deduplicationKey, eventType]
  );

  // Process the event (send email, update database, etc.)
  await handleWebhookEvent(event);
}

Testing before going live

Before disabling Paystack emails on your live plans:

  1. Build and test your email templates on a test plan with emails still enabled.
  2. Verify that your webhook handler sends emails correctly for all event types.
  3. Create a test subscription, let it renew, cancel it, and verify you sent the right emails.
  4. Only then disable Paystack emails on your production plans.

Transitioning from Paystack Emails to Your Own

If you already have customers on plans with Paystack emails enabled, transition carefully:

  1. Build your email system first. Get all templates working, webhook handlers tested, and deduplication in place.
  2. Run both in parallel for a week. Keep Paystack emails on. Send your emails too. Yes, customers get duplicates temporarily, but this lets you verify your system works for all event types.
  3. Disable Paystack emails. Update the plan settings to set send_invoices: false and send_sms: false.
  4. Monitor delivery rates. Check that your emails are reaching inboxes (not spam folders). Monitor bounce rates and open rates for the first few weeks.

If you have multiple plans, you can disable Paystack emails one plan at a time to reduce risk.

Notifications for Manual Recurring Charges

If you use charge_authorization instead of the Subscriptions API, Paystack sends a payment receipt email to the customer by default for each successful charge. This is separate from the subscription email settings.

For manual charges, Paystack's default receipts may be acceptable since they are transaction-level, not subscription-level. But if you want full control, send your own receipt after each charge_authorization call and coordinate with Paystack support about disabling default transaction receipts.

async function chargeAndNotify(userId, amount, authCode, purpose) {
  var user = await getUser(userId);
  var reference = 'BILL_' + userId + '_' + Date.now();

  var result = await chargeAuthorization(user.email, amount, authCode, reference);

  if (result.status === 'success') {
    // Send your own receipt
    await emailService.send({
      to: user.email,
      subject: 'Payment receipt',
      template: 'manual_charge_receipt',
      data: {
        customerName: user.first_name,
        amount: (amount / 100).toLocaleString(),
        purpose: purpose,
        reference: reference,
        date: new Date().toLocaleDateString(),
      },
    });
  } else {
    // Send failure notification
    var cardUpdateUrl = await generateCardUpdateUrl(user.email, userId);
    await emailService.send({
      to: user.email,
      subject: 'Payment did not go through',
      template: 'charge_failed',
      data: {
        customerName: user.first_name,
        cardUpdateUrl: cardUpdateUrl,
        reason: result.gateway_response,
      },
    });
  }

  return result;
}

The goal is always the same: the customer receives exactly one notification per billing event, branded with your logo and voice, with clear information about what happened and what to do next.

Key Takeaways

  • Paystack sends default email notifications for subscription events (charges, renewals, failures). These are functional but unbranded.
  • Disable Paystack emails by setting send_invoices: false and send_sms: false on the plan object.
  • Replace them with your own emails triggered by webhook events. This gives you control over branding, content, and timing.
  • The key webhooks for email triggers are: charge.success (receipt), invoice.payment_failed (dunning), subscription.create (welcome), and subscription.disable (farewell).
  • Always verify that Paystack emails are actually disabled before launching your own. Test with a real subscription to avoid duplicate notifications.
  • Keep Paystack emails enabled during development and initial testing. Only disable them when your own email system is reliable.

Frequently Asked Questions

How do I disable Paystack subscription emails?
Set send_invoices: false and send_sms: false when creating the plan, or update an existing plan with PUT /plan/:code. This stops Paystack from sending email and SMS notifications to customers on that plan.
Can I disable emails for some plans but not others?
Yes. Email settings are per-plan. You can have some plans with Paystack emails enabled (for simple products) and others with emails disabled (for products with custom branded emails).
What happens if I disable Paystack emails but my own email system fails?
Customers receive no notifications at all. This is worse than receiving Paystack's default emails. Only disable Paystack emails after your own email system is tested and reliable. Monitor delivery rates and set up alerts for email system failures.
Does disabling send_invoices also stop the customer management email?
The send_invoices setting controls automatic invoice and receipt emails. The subscription management link (using email_token) is a separate feature. Check Paystack documentation for the exact scope of each setting.
Can I customize Paystack default emails instead of replacing them?
Paystack does not offer customization of their default email templates (adding your logo, changing the text). If you want branded emails, you need to disable the defaults and send your own.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon