# How the Dub Referral Sale Sweep Complements Billing Webhooks for Renewals and Top‑Ups

> Discover how the Dub referral sale sweep and billing webhooks work together to accurately track subscription renewals and top ups for your business.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-09-12

---

**Open SEO employs a dual‑layer attribution strategy where billing webhooks capture initial referral sales in real time while a nightly Dub referral sale sweep catches subscription renewals and top‑up purchases that webhooks miss.**

Reliable referral tracking demands capturing both the first payment and all subsequent revenue events attributed to a Dub referral. The Open SEO codebase implements a resilient pipeline that combines immediate webhook processing with a periodic batch job, ensuring complete revenue attribution across the entire customer lifecycle.

## The Billing Webhook Path for Immediate Attribution

When a referred organization generates a paid invoice, the system must record that sale in Dub without delay. The Autumn billing webhook provides this real‑time capability by triggering attribution logic instantly upon invoice creation.

### Implementation in autumn-webhook.ts

The `autumnWebhookHandler` located in [`src/server/billing/autumn-webhook.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/autumn-webhook.ts) imports `trackDubSalesForOrganization` and invokes it immediately after processing a successful invoice. This ensures **prompt** attribution for first‑time paid subscriptions, capturing the initial conversion as it happens.

```typescript
import { trackDubSalesForOrganization } from "@/server/referrals/dub";

export async function autumnWebhookHandler(customerId: string) {
  // Process the Autumn billing webhook payload...
  await trackDubSalesForOrganization(customerId); // Instant attribution
}

```

## The Daily Dub Referral Sale Sweep

Autumn’s `billing.updated` event does not fire for subscription renewals or one‑time top‑up purchases. Relying solely on webhooks creates a data gap where recurring revenue and add‑on purchases go unattributed to the original referral.

### Why Webhooks Miss Renewals and Top‑Ups

According to the implementation comments in [`src/server/referrals/dub.ts`](https://github.com/every-app/open-seo/blob/main/src/server/referrals/dub.ts), Autumn’s webhook system lacks documentation confirming delivery for renewal events or one‑time top‑ups. Without a secondary catch‑mechanism, these critical revenue events—essential for accurate lifetime value calculations—would be permanently lost.

### How the Sweep Captures Missed Revenue

The `sweepDubReferredOrganizations` function runs as a nightly cron job. It iterates over every organization carrying a Dub referral pin, examining invoices created within the last **45 days**. For each qualifying invoice, the function constructs a Dub sale request and calls `trackDubSale`, ensuring that renewals, top‑ups, and invoices that were still pending when the original webhook fired are properly recorded.

```typescript
import { sweepDubReferredOrganizations } from "@/server/referrals/dub";

export async function dailyCron() {
  // Executes once per day
  await sweepDubReferredOrganizations(); // Catches renewals and top-ups
}

```

## Key Implementation Files

Referral attribution logic spans several dedicated modules:

- **[`src/server/referrals/dub.ts`](https://github.com/every-app/open-seo/blob/main/src/server/referrals/dub.ts)** – Core integration housing `trackDubSalesForOrganization`, `sweepDubReferredOrganizations`, and the daily sweep logic.
- **[`src/server/billing/autumn-webhook.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/autumn-webhook.ts)** – Receives Autumn billing events and triggers immediate sale tracking for new invoices.
- **[`src/server/referrals/dub-sale.ts`](https://github.com/every-app/open-seo/blob/main/src/server/referrals/dub-sale.ts)** – Constructs the payload sent to Dub’s `/track/sale` endpoint, used by both the webhook and sweep paths.
- **[`src/server/lib/posthog.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/posthog.ts)** – Helper for capturing server‑side errors when Dub API calls fail.

## How Both Systems Work Together

The **immediate webhook** captures the *first* paid event when it occurs, providing real‑time attribution for new subscriptions. The **daily sweep** guarantees that any subsequent revenue events—renewals, top‑ups, or late‑closing invoices—that the webhook cannot see are still attributed to the original Dub referral. This architecture operates **idempotently**, ensuring that running both systems concurrently creates comprehensive coverage without duplicate records.

## Summary

- **Billing webhooks** in [`src/server/billing/autumn-webhook.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/autumn-webhook.ts) provide instant attribution by calling `trackDubSalesForOrganization` immediately after invoice creation.
- **Daily sweep** in [`src/server/referrals/dub.ts`](https://github.com/every-app/open-seo/blob/main/src/server/referrals/dub.ts) executes `sweepDubReferredOrganizations` to catch renewals and top‑ups that Autumn webhooks miss, scanning invoices from the last 45 days.
- The combined approach ensures reliable, duplicate‑free tracking of all referral revenue regardless of whether it originates from new subscriptions, renewals, or one‑time purchases.

## Frequently Asked Questions

### Why doesn't the billing webhook catch subscription renewals?

Autumn’s `billing.updated` event is not documented to fire for subscription renewals or one‑time top‑up purchases according to comments in [`src/server/referrals/dub.ts`](https://github.com/every-app/open-seo/blob/main/src/server/referrals/dub.ts). Without the daily Dub referral sale sweep, these revenue events would remain unattributed to the originating referral.

### How does Open SEO prevent duplicate sale tracking?

Both the webhook handler and the daily sweep utilize the same `trackDubSale` function, which operates idempotently. Processing the same invoice twice—once via webhook and once via sweep—does not create duplicate records in Dub, ensuring data integrity while maximizing attribution coverage.

### What is the 45‑day lookback window for?

The `sweepDubReferredOrganizations` function examines invoices created within the last 45 days to balance comprehensiveness against performance. This window captures invoices that were still open when the webhook originally fired, as well as recent renewals and top‑ups, without reprocessing excessive historical data.

### Where is the Dub sale payload constructed?

The payload sent to Dub’s `/track/sale` endpoint is built in [`src/server/referrals/dub-sale.ts`](https://github.com/every-app/open-seo/blob/main/src/server/referrals/dub-sale.ts). This module is shared between the billing webhook path and the daily sweep, ensuring consistent data formatting and API interaction regardless of which system initiates the tracking call.