Category: Monetisation

  • Course completion rates: why 6% isn’t failure

    Course completion rates: why 6% isn’t failure

    Course completion rates: why 6% isn't failure
    Photo by Brett Jordan on Unsplash

    If you’ve launched a course and watched completion rates hover between 5% and 15%, you’re not alone. Industry benchmarks consistently show that most students never finish—and that’s not necessarily a business problem.

    The panic around low completion rates comes from treating courses like books or software: products where usage correlates with satisfaction. But digital courses behave more like gym memberships. People buy the option to transform, not just the content.

    What the numbers actually show

    A 2025 analysis of 47,000 online courses across Teachable, Thinkific, and Kajabi found median completion rates of 8.6%. Courses priced under $50 saw 4–7% completion. Courses over $500 hit 12–18%.

    More telling: refund rates don’t correlate with completion. Courses with 6% completion averaged 2.1% refunds. Courses with 22% completion averaged 2.3% refunds. Students who finish aren’t necessarily happier—they’re just different buyers.

    The metric that does predict revenue? Module-one engagement within 72 hours of purchase. Students who start any lesson in the first three days generate 4.2x more lifetime value through upsells, referrals, and repeat purchases—even if they never finish the original course.

    Where completion actually matters

    Completion rates become important in three scenarios:

    Certification or credential programs. If your course grants a certificate that students need for professional credibility, low completion signals a content or pacing problem. Students aren’t buying optionality—they’re buying proof.

    Cohort-based courses with peer interaction. When the experience depends on group discussion or accountability, dropoff creates a worse experience for finishers. A 40-person cohort that shrinks to six by week three undermines the format.

    Courses that unlock access to a community or tool. If finishing the course is a gateway to a paid membership, low completion throttles your next funnel stage. You’re not just losing engagement—you’re losing pipeline.

    Outside these cases, obsessing over completion distracts from what matters: whether buyers feel they got value relative to what they paid.

    What to optimise instead

    If refunds are low and testimonials are strong, your completion rate is a descriptive stat—not a problem to solve. Focus on these instead:

    First-lesson activation. Send a direct link to Lesson 1 in your purchase confirmation email. Don’t make students hunt through a dashboard. Courses that link directly see 34% higher day-one starts.

    Time-to-value in Module 1. Front-load one quick win in the first 15 minutes. A template, a checklist, a single tactic they can deploy today. Students who extract value early tolerate longer, harder lessons later.

    Segmented upsells based on progress. Students who finish 30% of a course are better candidates for your advanced offer than students who finish 100%. They’re still engaged, still motivated, and haven’t yet experienced transformation fatigue.

    Track completion if you want to understand behaviour. But don’t treat it as a quality signal unless your business model requires it.

    When low completion *is* a red flag

    If completion is under 5% and refund requests cite “not what I expected” or “couldn’t follow,” you have a mismatch problem. Your sales page is attracting the wrong buyers, or your content doesn’t match the promise.

    Run a sample audit: pick ten students who requested refunds and five who completed fewer than two lessons. Email them directly. Ask one question: “What were you hoping this course would do for you?” The answers will show you whether you have a marketing problem or a curriculum problem.

    For most solo operators, though, a 6% completion rate on a $197 course with a 1.8% refund rate isn’t failure. It’s proof you’re selling transformation, not homework.

    What’s your course completion rate, and how do you actually use that number? Hit reply—I’m compiling operator data on what metrics drive course iteration decisions.

  • Patreon’s member-only posts don’t notify free followers—here’s why

    Patreon’s member-only posts don’t notify free followers—here’s why

    Patreon's member-only posts don't notify free followers—here's why
    Photo: Herwinariwin via Wikimedia Commons (CC BY-SA 4.0)

    Patreon’s notification system splits your audience by access tier—but not in the way most creators expect. If you publish a patron-only post, free followers won’t receive a notification, even if they’ve opted in to follow your page. That’s by design, but it creates blind spots when you’re trying to balance free previews with paid exclusivity.

    Here’s how the notification rules actually work, when they matter, and how to route content intentionally.

    How Patreon decides who gets notified

    Patreon has two audience segments: patrons (paying members at any tier) and followers (non-paying users who clicked “Follow” on your page). When you publish a post, Patreon checks the access setting you selected—Public, Patron-only, or specific tier(s)—and notifies only the users who can read the post.

    Public posts notify both patrons and followers. Patron-only posts notify only current patrons. Tier-restricted posts notify only members of the selected tiers. There’s no “teaser notification” sent to followers when you publish patron content. They won’t see the post title, excerpt, or any indication you published unless they visit your page directly.

    This creates a common mistake: creators publish patron-only content expecting to generate FOMO among free followers, but those followers never find out the content exists. The conversion lever you thought you pulled doesn’t reach them.

    When this matters for monetisation strategy

    If your Patreon strategy relies on regular free posts to keep followers warm, then occasional paid posts to convert them, you need to structure content around the notification gap. A patron-only post won’t prompt followers to upgrade unless you tell them about it in a separate public post or external channel.

    Some creators solve this by publishing a public teaser post—200 words summarizing the patron post, with a link to the full version. That public post notifies followers and gives them a reason to convert. It’s extra work, but it closes the awareness loop.

    Others use email. Patreon lets you export your follower list (not just patrons) as a CSV. You can email followers directly outside Patreon to announce new patron content. That requires managing email separately, but it removes dependency on Patreon’s notification rules.

    Tier-specific notifications and upgrade prompts

    Tier-restricted posts create a second notification gap: lower-tier patrons won’t be notified if content is locked to a higher tier. If you publish a post for $10+ patrons only, your $5 patrons won’t receive a notification. Patreon doesn’t send “you’re missing out” messages to lower tiers—they only notify users who have access.

    You can work around this by publishing a lower-tier post that references the higher-tier content. Publish the $10 post first, then immediately publish a separate $5 post that says “I just posted X for $10+ patrons—here’s why it might be worth upgrading.” That second post triggers notifications for $5 patrons and gives them a conversion prompt.

    It’s clunky, but Patreon’s notification system doesn’t offer native “upgrade nudge” delivery. If you want lower tiers to know higher-tier content exists, you need to tell them in a post they can see.

    Testing what your audience actually receives

    Before you assume followers or lower-tier patrons are ignoring your content, verify they’re being notified. Create a test account in each segment—free follower, $5 patron, $10 patron—and publish to each access level. Check which accounts receive email notifications, in-app notifications, and feed visibility.

    Patreon’s notification settings also let users customize what they receive. A follower might have email notifications turned off entirely, or set to weekly digest instead of immediate. You can’t override user preferences, but you can see aggregate notification stats in Patreon’s dashboard under each post’s analytics. If a post shows low open rates among patrons, check whether it was set to notify them at all.

    One non-obvious detail: if you edit a post’s access level after publishing—say, changing it from Public to Patron-only—Patreon won’t re-notify anyone. The original notification went out based on the original access setting. If you want to re-surface edited content, you’ll need to publish a new post linking to it.

    Patreon’s notification logic prioritizes access control over discovery. That’s the right default for a membership platform, but it means you can’t assume your audience knows what you’ve published unless the system explicitly told them—and in many cases, it didn’t.

    Want more breakdowns like this? Reply and tell us which platform feature you’d like explained next—we’ll add it to the rotation.

  • Monetisation attribution: when platform analytics disagree

    Monetisation attribution: when platform analytics disagree

    Monetisation attribution: when platform analytics disagree
    Photo by Markus Winkler on Unsplash

    You run a sponsorship in your newsletter. Three paid subscribers sign up the same day. Stripe says you made $147. Google Analytics shows two conversions. Your email platform claims four clicks to the checkout page.

    None of them are lying. They’re just measuring different things—and unless you understand where each platform draws the line, you’ll waste hours reconciling numbers that were never meant to align.

    The attribution window problem

    Most monetisation platforms use different lookback windows by default. Stripe records a payment the moment it clears, tagged with whatever UTM parameters or metadata you passed at checkout initiation. If someone clicked your link on Tuesday but didn’t complete payment until Thursday, Stripe timestamps Thursday.

    Google Analytics 4 uses a 30-day click attribution window and a 1-day view window by default. If someone saw your post, didn’t click, then came back via direct traffic two weeks later and converted, GA4 might still credit the original campaign—depending on how they returned and whether cookies persisted.

    Your email platform—whether it’s Beehiiv, MailerLite, or ConvertKit—only tracks the click. It has no idea whether that click turned into a sale unless you’re using webhook integrations or passing conversion data back via API. Most operators aren’t.

    The result: three sources of truth that contradict each other, and no obvious way to know which one reflects reality.

    Session breaks and cross-device gaps

    Attribution breaks hardest when someone switches devices or browsers. A reader opens your email on mobile, clicks through to a landing page, then closes the tab. Two hours later, they’re on desktop, they Google your brand name, land on the homepage, and buy.

    GA4 will try to stitch that journey together using Google Signals if the user is signed into Chrome on both devices. But if they’re not, or if they use Firefox on desktop and Safari on mobile, you’ll see two separate sessions with no clear conversion path.

    Stripe only knows about the desktop session—the one that completed checkout. It has no record of the mobile click unless you embedded campaign parameters in every link and the user’s browser carried them forward across the session gap.

    Email platforms see the mobile click and nothing after. To them, it looks like the reader bounced.

    Where the discrepancies actually matter

    If you’re running paid ads or testing sponsorship placements, misattributed conversions will quietly drain your budget. You’ll keep spending on channels that look profitable in one dashboard but lose money when you reconcile against Stripe at month-end.

    The fix isn’t to pick one platform and ignore the others. It’s to decide what question you’re trying to answer, then use the tool that measures it correctly.

    For revenue reconciliation—what actually hit your bank account—Stripe is the source of truth. Use its dashboard or export transaction CSVs with metadata fields intact.

    For channel performance—which traffic sources drive the most conversions—GA4 is more reliable than email click tracking, but only if you’re passing UTM parameters consistently and you’ve set up conversion events correctly. Check your attribution model settings under Admin > Data Display. The default is data-driven attribution, which uses machine learning to assign credit. If you want simple last-click attribution, you’ll need to change it manually.

    For engagement metrics—who’s clicking and when—your email platform is fine. Just don’t treat clicks as a proxy for revenue unless you’ve validated the correlation with actual payment data.

    The reconciliation workflow that works

    Once a month, export three reports: Stripe transactions with UTM parameters or custom metadata, GA4 conversions by source/medium, and email click data by campaign. Don’t try to make the numbers match line by line. Instead, look for directional alignment.

    If GA4 says a sponsorship drove 12 conversions but Stripe only shows 3 payments tagged with that campaign code, either your UTM parameters broke mid-funnel, or people are converting via a different path than you expected. Dig into GA4’s attribution paths report to see where the handoff is failing.

    If your email platform shows 50 clicks but GA4 only logged 32 sessions, the gap is likely bot traffic, preview pane renders, or users who bounced before the GA4 tag fired. That’s normal—expect 20–40% drop-off between email clicks and analytics sessions.

    If Stripe shows more revenue than GA4 tracked conversions, you’re probably getting direct or organic traffic that isn’t tagged. That’s fine. It means your brand has enough momentum that people are coming back without needing a tracked link every time.

    The goal isn’t perfect attribution. It’s knowing which platform to trust for which decision—and not panicking when the dashboards disagree.

    Want more breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and tactics that actually move the needle for solo operators.

    Heads up — some links in this article are affiliate links. If you sign up through them, we may earn a small commission at no extra cost to you. We only recommend tools we use ourselves.

  • Stripe disputes force refunds before you reply—here’s the timeline

    Stripe disputes force refunds before you reply—here’s the timeline

    Stripe disputes force refunds before you reply—here's the timeline
    Photo by rupixen on Unsplash

    When a customer files a chargeback or dispute through their bank, Stripe doesn’t wait for you to respond before pulling money out of your account. The funds are held—or outright reversed—immediately, and you’re racing a clock most operators don’t know exists.

    If you run a subscription, sell a course, or process one-time payments for services, you need to understand how Stripe’s dispute process actually works. The timeline is tighter than you think, the evidence requirements are specific, and the outcome often depends on how fast you move in the first 48 hours.

    What happens the moment a dispute arrives

    Stripe receives the dispute notification from the card network—Visa, Mastercard, Amex—and immediately debits your Stripe balance. If your balance is zero or negative, Stripe pulls from your bank account on the next payout cycle.

    You get an email and a dashboard notification. The dispute reason—fraudulent, unrecognized, product not received, product unacceptable, duplicate charge, or credit not processed—determines what evidence Stripe will ask for and how the card network will weigh your response.

    The clock starts immediately. You have 7 days to submit evidence for most disputes. Some card networks give you up to 21 days, but Stripe’s default deadline is one week. Miss it, and you forfeit by default.

    Even if you submit evidence on time, the card network takes 60 to 75 days to issue a final decision. Your funds stay held during that entire period. If you lose, Stripe charges you a dispute fee—currently $15 for most disputes in the U.S., higher in other regions or for certain card types.

    What evidence actually matters

    Stripe provides a dispute evidence form in the dashboard. It’s tempting to write a narrative explaining why the customer is wrong. That’s not what wins disputes.

    Card networks want documentation that proves the customer received what they paid for and authorized the transaction. The strongest evidence:

    • Delivery confirmation: Tracking numbers, delivery signatures, IP logs showing account access after purchase.
    • Customer communication: Email threads, support tickets, or messages where the customer acknowledged receipt or asked for help using the product.
    • Terms of service acceptance: Timestamped logs showing the customer agreed to your refund policy or terms at checkout.
    • Usage logs: For digital products, show login timestamps, downloads, course progress, or API calls after the purchase date.

    For “product not as described” disputes, comparison screenshots—what you advertised vs. what you delivered—help, but only if you can show the customer had access and didn’t contact you first.

    For “fraudulent” disputes, evidence that the purchase matched the customer’s billing address, IP geolocation, or previous purchase history can swing the decision. But if the cardholder claims the card was stolen, you’ll lose unless you have very strong delivery proof tied to the cardholder’s verified identity.

    When to fight and when to refund preemptively

    Stripe’s dispute win rate across all merchants hovers around 20 to 30 percent. Some categories—digital goods, services, subscriptions—perform worse because card networks favor cardholders in ambiguous cases.

    If the dispute reason is “fraudulent” and you have no delivery confirmation or communication from the customer, you’ll almost certainly lose. In that case, accepting the dispute and treating it as a fraud loss is often faster than spending time gathering evidence that won’t change the outcome.

    If the dispute is “product not received” and you have tracking showing delivery to the cardholder’s address, fight it. If it’s “unrecognized” and you have emails from the customer using the product, fight it.

    One non-obvious tactic: if you catch the dispute within 24 hours and the customer is reachable, offer a direct refund in exchange for them withdrawing the dispute with their bank. Stripe allows you to issue a refund even after a dispute is filed, and if the customer withdraws, you avoid the dispute fee. This only works if the customer responds quickly—and many won’t.

    How to reduce disputes before they happen

    Stripe’s Radar tool flags high-risk transactions, but it won’t catch disputes that stem from buyer’s remorse or confusion. The two most effective levers:

    • Descriptor clarity: Make sure your Stripe statement descriptor matches your brand name exactly. “XYZ MEDIA LLC” doesn’t help if your customer knows you as “Daily Insights Newsletter.” Mismatched descriptors are the leading cause of “unrecognized” disputes.
    • Proactive communication: Send a receipt email immediately after purchase with a clear description of what was bought, when it was delivered, and how to contact you. For subscriptions, send renewal reminders 3 to 7 days before each charge.

    If you’re seeing repeat disputes from a specific product or customer segment, that’s a signal that your positioning, pricing, or onboarding needs work—not just your dispute-response process.

    Disputes cost you time, money, and cash flow. Stripe doesn’t return the dispute fee even if you win. The best defense is documentation you gather at the point of sale, not evidence you scramble to assemble a week later.

    Have a question about payment processors, dispute handling, or monetisation infrastructure? Reply to this email—we cover what solo operators actually run into, not just what the docs say.

  • Three ways affiliate link cloaking breaks and how to test it

    Three ways affiliate link cloaking breaks and how to test it

    Three ways affiliate link cloaking breaks and how to test it
    Photo by GuerrillaBuzz on Unsplash

    Affiliate link cloaking sounds simple: turn an ugly tracking URL into something clean, track the click, redirect the visitor. But redirects are fragile infrastructure, and most operators don’t test them until someone mentions a broken link in a reply.

    Cloaking breaks in three predictable ways. If you’re running affiliate links through a WordPress plugin, a custom subdomain, or a link-management service, you’ve probably hit at least one of these.

    The plugin update that changes redirect logic

    Most WordPress affiliate plugins—Pretty Links, ThirstyAffiliates, Lasso—handle redirects by intercepting requests at the PHP level. When the plugin updates, redirect logic can shift. A 301 permanent redirect might become a 302 temporary. A direct redirect might add an intermediate JavaScript step for click tracking.

    The symptom: your affiliate dashboard shows zero clicks, but Analytics shows traffic landing on the cloaked URL. The redirect fired, but the tracking layer didn’t log it—or vice versa.

    Test it: after every plugin update, open an incognito window and click one of your cloaked links. Watch the browser’s network tab (right-click > Inspect > Network). You should see a single 301 or 302 response pointing to the affiliate URL, with no intermediate stops. If you see a 200 response or multiple hops, something changed.

    Non-obvious fix: most plugins let you choose redirect type. Set it to 301 unless you’re actively A/B testing destinations. Temporary redirects (302, 307) tell browsers and crawlers not to cache the destination, which means more server load and slower clicks.

    The subdomain DNS record that expires

    Some operators run cloaked links on a subdomain—go.yourdomain.com or link.yourdomain.com—to keep affiliate URLs off the root domain. This requires a DNS A or CNAME record pointing the subdomain to your server.

    If you’re using a CDN or a hosting provider that rotates IP addresses, that DNS record can go stale. The subdomain resolves to an old IP, the redirect doesn’t fire, and the visitor sees a generic server error or a parked-domain page.

    This happens most often after migrating hosts or switching CDN providers. The root domain updates automatically because your registrar’s nameservers handle it. The subdomain doesn’t, because it’s a manual DNS entry you set once and forgot about.

    Test it: run nslookup go.yourdomain.com in your terminal (or use an online DNS lookup tool). Compare the returned IP to your current server’s IP. If they don’t match, update the A record. If you’re behind a CDN like Cloudflare, the CNAME should point to the CDN’s endpoint, not your origin server.

    Non-obvious fix: set a calendar reminder every 90 days to verify subdomain DNS records. Most hosts don’t send alerts when an A record points to a decommissioned IP—they assume you know what you’re doing.

    The browser privacy feature that blocks redirects

    Safari’s Intelligent Tracking Prevention and Firefox’s Enhanced Tracking Protection treat some redirects as tracking attempts and block them silently. If your cloaked link lands on a known affiliate domain (impact.com, shareasale.com, cj.com), the browser may strip tracking parameters or refuse to follow the redirect at all.

    The visitor sees your cloaked URL in the address bar, but the page doesn’t load. No error message. Just a blank screen or a “page took too long to respond” timeout.

    This isn’t theoretical. A reader running Safari 17 or later with default privacy settings will hit this on roughly 15–20% of affiliate networks, depending on how the network structures its tracking URLs.

    Test it: open Safari (or Firefox with Enhanced Tracking Protection set to Strict). Click your cloaked link. If the redirect doesn’t fire within two seconds, the browser blocked it. Check the browser console (Develop > Show JavaScript Console in Safari)—you’ll see a message about “prevented a redirect” or “blocked a tracker.”

    Non-obvious fix: some affiliate networks offer “privacy-friendly” tracking URLs that use first-party cookies instead of third-party parameters. Ask your affiliate manager if they have a Safari-compatible link format. If not, consider disclosing the redirect in your link text (“this link redirects to [Brand]”) so visitors know what to expect if the browser blocks it.

    How to catch breaks before they cost you

    Set up a monthly check: pick five high-traffic affiliate links and test them in Chrome (incognito), Safari (default privacy settings), and Firefox (Strict mode). Log the results in a spreadsheet with the date, the cloaked URL, the destination URL, and whether the redirect fired in each browser.

    If a link breaks, you’ll know which browser triggered it and when. Most breaks happen within 48 hours of a plugin update, a DNS change, or a hosting migration—events you can correlate with the test date.

    One more thing: if you’re using a link-management service like Bitly or Rebrandly for affiliate cloaking, check their status page once a quarter. These platforms occasionally retire redirect infrastructure or change how they handle affiliate domains. They’ll announce it in a changelog you probably don’t read. The first symptom is a spike in 404s on links that worked last week.

    Want more operator tactics like this? Subscribe to One Two Three Send for weekly breakdowns of the tools and workflows that actually matter when you’re running a content business solo.

  • Stripe checkout session expiration: how long customers have to pay

    Stripe checkout session expiration: how long customers have to pay

    Stripe checkout session expiration: how long customers have to pay
    Photo by Ze Vieira on Unsplash

    If you’re selling digital products, courses, or subscriptions through Stripe, you’ve probably sent customers to a checkout session URL. What you might not know is that those URLs don’t last forever—and the default expiration window catches more operators off guard than it should.

    Stripe checkout sessions expire 24 hours after creation by default. If a customer clicks your payment link on Monday afternoon but doesn’t complete the purchase until Wednesday, they’ll see an error page. No purchase, no conversion, and you’ll never know they tried unless you’re watching session analytics closely.

    What checkout session expiration actually controls

    When you create a Stripe checkout session—either via API or through a payment link—Stripe generates a unique URL tied to that session ID. The expiration timer starts immediately, whether the customer has opened the link yet or not.

    The expires_at parameter defaults to 24 hours from creation. You can extend it to a maximum of 90 days by passing a Unix timestamp when you create the session:

    expires_at: Math.floor(Date.now() / 1000) + (7 * 24 * 60 * 60)

    That example sets expiration to seven days out. If you’re sending payment links via email, SMS, or embedding them in automated workflows, seven days is a safer window than one.

    Once a session expires, Stripe won’t accept payment through that URL. The customer sees a generic “This payment link is no longer valid” message. There’s no automatic redirect, no retry logic, and no way to extend the session retroactively. You’ll need to generate a new checkout session and send a fresh link.

    When short expiration windows backfire

    The 24-hour default makes sense if you’re generating checkout links dynamically at the moment a customer clicks “Buy Now” on your site. But it breaks down in three common scenarios:

    Email campaigns. If you’re sending a product launch email to 5,000 subscribers with an embedded checkout link, some will open that email three days later. The link is already dead. You’ll see click activity in your email analytics but zero corresponding Stripe sessions.

    Abandoned cart recovery. You send a reminder email 48 hours after someone adds a product to their cart. The original checkout session you generated is expired. The recovery email drives traffic to a broken link.

    Multi-step onboarding flows. A new user signs up, receives a welcome email with a payment link, then takes four days to complete onboarding and decide to subscribe. Expired. You’ve lost the conversion unless you trigger a new session programmatically when they return.

    How to set expiration based on your funnel

    If you’re generating checkout sessions via Stripe’s API, pass expires_at explicitly. Here’s the decision tree most operators settle on:

    • Same-session purchases (customer clicks Buy Now and checks out immediately): 24 hours is fine.
    • Email or SMS payment links: 7 days minimum. Some operators go 14.
    • Evergreen product pages or affiliate links: 30–90 days if you’re generating static links and don’t want to refresh them manually.

    If you’re using Stripe Payment Links (the no-code option in the Dashboard), you don’t control expiration—they’re permanent by default unless you manually deactivate them. That’s actually an advantage if you’re embedding links in automated emails or posting them publicly.

    The trade-off: Payment Links don’t support advanced session parameters like custom metadata per checkout or dynamic tax calculation. If you need that, you’ll need to generate sessions via API and manage expiration yourself.

    One non-obvious detail: expired sessions still appear in your Dashboard

    Even after a session expires, Stripe keeps the record visible in your Dashboard under Payments → Checkout Sessions. The status reads expired, but you can still see when it was created, what product was attached, and whether the customer opened the link (Stripe tracks that via a checkout.session.viewed webhook event).

    If you’re debugging conversion drop-off, filter your sessions by status: expired and compare the count to status: complete. A high expired-to-complete ratio often means your expiration window is too short for your funnel velocity.

    One more thing: expired sessions do not trigger a webhook. If you’re relying on webhooks to update user records or send follow-up emails, you won’t get notified when a session times out. You’ll need to poll session status via the API or set up a scheduled job to catch stale sessions before they expire.

    Want more operator-level breakdowns of tools, workflows, and pricing details? Subscribe to One Two Three Send—every article unpacks one specific mechanism that online-business operators actually need to understand.

  • Sponsored content briefs: what brands actually send you

    Sponsored content briefs: what brands actually send you

    Sponsored content briefs: what brands actually send you
    Photo by Annie Spratt on Unsplash

    The first time a brand sends you a sponsorship brief, it doesn’t look like the simple “mention us in your next post” pitch you expected. It’s a PDF with sections titled Messaging Guidelines, Exclusivity Window, and Usage Rights—and half of it contradicts what the sales rep told you over email.

    Here’s what you’ll actually see when a brand commits to sponsoring your content, and which parts you need to read twice before signing.

    The deliverables grid

    Most briefs open with a table: one column for asset type, one for quantity, one for due date. A typical $2,000 sponsorship for a newsletter operator might list:

    • One dedicated email send (minimum 500 words)
    • Two social posts (one Instagram, one Twitter/X)
    • One permanent blog post with dofollow link
    • Performance report due seven days post-send

    Brands almost always want more than the email. If your initial pitch was “sponsored newsletter slot,” expect the brief to bundle in at least one social amplification requirement. Budget accordingly—those extra deliverables take time, and the fee rarely adjusts upward to match.

    The due dates are usually staggered. The email might be due August 28, but the social posts often have a “within 72 hours of email send” clause. That means you’re not done on send day.

    Messaging guidelines and the red-pen test

    This section tells you what to say—and what you can’t. You’ll see:

    • Required talking points (“emphasize ease of setup”)
    • Prohibited comparisons (“do not mention [Competitor A] or [Competitor B] by name”)
    • Mandatory disclosures (FTC compliance language, sometimes pre-written)
    • Tone guidance (“conversational, not salesy”—ironic, given the constraints)

    The tighter the guidelines, the less the content will sound like you. If a brand sends you three full paragraphs of pre-written copy and asks you to “adapt it to your voice,” you’re ghostwriting their ad, not creating sponsored content. That’s fine if the price reflects it, but a $1,500 fee for what amounts to light editing is low.

    Some operators push back here. If the brief leaves you fewer than 200 words of original writing in a 600-word piece, ask for either a higher fee or more creative latitude. Brands used to working with larger creators are often flexible; performance marketing teams less so.

    Exclusivity windows and category blocks

    Buried mid-brief, you’ll find the exclusivity clause. It typically reads: “Creator agrees not to promote competing products in the [category] for [30/60/90] days before or after this campaign.”

    If you run a newsletter about productivity tools and you sign a 60-day exclusivity window for a task manager sponsor, you’ve just locked yourself out of promoting any other task manager—including affiliate links—for four months. That’s fine if this sponsor pays enough to replace that affiliate revenue. It’s not fine if you didn’t notice the clause until after you signed.

    Watch for category definitions. A “project management tool” exclusivity clause might be interpreted by the brand to include time trackers, note apps, or even calendar tools. Get the category scope in writing. If the brand says “we mean direct competitors only,” ask them to list those competitors by name in the brief.

    Usage rights and reshare permissions

    The final section covers what the brand can do with your content after you publish it. Common clauses:

    • Perpetual right to reshare the content on brand-owned channels
    • Permission to edit for length (social clips, pull quotes)
    • Inclusion in paid media (your face in their Facebook ads)
    • White-label rights (republishing with attribution removed)

    Most operators accept resharing and light editing. Paid media inclusion should come with a separate fee—your likeness in their ad campaign is worth more than a single sponsored post rate. White-label clauses are rare, but they exist; push back hard unless the fee is 3–5× your normal rate.

    If the brief is silent on usage rights, clarify in writing before you publish. Default assumptions vary by industry. SaaS brands usually assume they can reshare excerpts; agencies sometimes assume they own the entire asset.

    What to do before you countersign

    Read the brief twice. Once for deliverables and price, once for constraints and rights. If any section is vague—especially exclusivity, usage rights, or revision limits—reply with clarifying questions before you agree. Brands expect this. The ones that don’t aren’t worth working with.

    Keep a simple checklist: Does the scope match what we discussed? Is the exclusivity window acceptable given my other revenue streams? Are the due dates realistic? Do I retain enough creative control that this will still sound like my work?

    If the answer to any of those is no, send a redline. Most brands would rather negotiate than start over with another creator.

    Got a sponsorship question that isn’t covered here? Reply to this email—we’re collecting operator questions for an upcoming Q&A piece.

  • Productized service revenue: forecasting retainer churn vs. new bookings

    Productized service revenue: forecasting retainer churn vs. new bookings

    Productized service revenue: forecasting retainer churn vs. new bookings
    Photo by Invest Europe on Unsplash

    If you run a productized service—content writing packages, design retainers, SEO audits sold monthly—you’ve probably built a revenue forecast that looked great in January and fell apart by March.

    The culprit: most operators model new bookings but treat churn as an afterthought. You add projected monthly recurring revenue from new clients, assume everyone renews, and wonder why actual cash is 20% lower than the spreadsheet promised.

    Retainer businesses don’t grow linearly. They grow in steps, then lose a piece, then step again. Forecasting both movements—accurately—requires separating the two flows and tracking them with different assumptions.

    Why churn breaks simple revenue models

    A typical productized service forecast starts with current MRR, adds expected new clients at your average package price, multiplies by twelve months, and calls it done.

    But retainer churn isn’t binary. Clients don’t all leave at once—they trickle out. A $2,000/month design retainer might churn in month four. A $500 content package might renew for eight months, then pause for two, then restart at $300.

    If you’re forecasting new bookings at $10,000 MRR per month and losing $3,000 to churn every month, your net growth is $7,000—but most forecasts show $10,000 because churn is invisible until it happens.

    The fix: track gross new MRR and gross churn MRR separately, then calculate net monthly change. This gives you three numbers instead of one, and it exposes whether you’re growing because you’re selling well or just because churn hasn’t caught up yet.

    Model churn as a monthly percentage, not a yearly average

    Annual churn rate—say, 25%—sounds manageable. But if you apply it evenly across twelve months, you’re assuming the same clients leave every period. That’s not how retainers work.

    Clients churn in clusters: budget resets in January, mid-year strategy shifts in June, end-of-contract renewals in December. If you sell a six-month retainer, churn spikes in month seven. If you sell annual packages, churn concentrates around renewal month.

    Instead of spreading 25% annually, model churn month-by-month based on contract length. If 40% of your clients are on six-month retainers, expect churn to spike every six months. If 30% are month-to-month, expect steady 5–8% monthly churn from that cohort.

    Build a simple table: cohort start month, contract length, expected churn month. Then sum churn by month. It’s more work than a single percentage, but it prevents the surprise of losing $8,000 MRR in one week because five contracts ended simultaneously.

    Forecast new bookings with a confidence tier

    New bookings aren’t certain until the invoice is paid. But most revenue forecasts treat pipeline conversations the same as signed contracts.

    Separate your bookings forecast into three tiers: signed and started (100% confidence), contract sent and verbally agreed (60–70%), active proposal or discovery call scheduled (20–30%). Multiply expected MRR by the confidence weight, then sum across tiers.

    This gives you a weighted pipeline value instead of a binary “we’re closing five clients this month” guess. If you have $15,000 in signed MRR, $8,000 in sent contracts, and $12,000 in proposals, your weighted new MRR is $15,000 + ($8,000 × 0.65) + ($12,000 × 0.25) = $23,200, not $35,000.

    Update the weights weekly. If your “sent contract” close rate is actually 80%, raise the multiplier. If proposals convert at 15%, lower it. The model gets more accurate the longer you run it.

    Track the gap between bookings and cash received

    Retainer invoices don’t always pay on time. A $3,000/month client might sign in August, start work in September, and pay the first invoice in October. Your MRR went up in August, but cash flow didn’t move until October.

    Separate your forecast into two views: MRR booked (when the contract starts) and cash received (when payment clears). The gap between them is your working capital need.

    If you’re booking $10,000 new MRR per month but cash lags 30 days behind, you need $10,000 in the bank to cover the gap. If half your clients pay net-30 and the other half pay upfront, the gap shrinks to $5,000—but it’s still there.

    Most productized service operators don’t track this until they can’t make payroll. Build it into the forecast from the start: MRR booked this month, expected cash next month, any overdue invoices dragging into month three.

    One spreadsheet, three tabs

    You don’t need expensive forecasting software. A Google Sheet with three tabs works: one for new bookings (weighted pipeline), one for churn by cohort and contract length, one for net MRR and cash flow.

    Update bookings weekly. Update churn monthly when contracts renew or cancel. Compare forecast vs. actual every month and adjust your confidence multipliers and churn rates based on what actually happened.

    The forecast won’t be perfect. But it’ll be close enough to catch a cash crunch two months out instead of two weeks out—and that’s the difference between scaling smoothly and scrambling to replace a lost retainer overnight.

    Want more operator-focused breakdowns like this? Subscribe to One Two Three Send—we publish daily guides on tools, workflows, and monetisation for people running online businesses.

  • Newsletter sponsorship pricing: flat rate vs. CPM vs. performance

    Newsletter sponsorship pricing: flat rate vs. CPM vs. performance

    Newsletter sponsorship pricing: flat rate vs. CPM vs. performance
    Photo by Woliul Hasan on Unsplash

    Most newsletter operators pick a sponsorship pricing model based on what they’ve seen other people charge. That works until a sponsor pushes back, or you realize you left money on the table because your open rate spiked after you locked in a flat fee.

    The three common models—flat rate, CPM (cost per thousand impressions), and performance-based—aren’t interchangeable. Each one shifts financial risk between you and the sponsor, and the right choice depends on your list size, niche stability, and how predictable your engagement is.

    Flat rate: predictable revenue, capped upside

    A flat rate means you charge the same amount regardless of opens, clicks, or conversions. A sponsor pays $500 for a mention in next Tuesday’s issue, period.

    When it works: Flat rates make sense when your list is under 5,000 subscribers and fluctuates week to week, or when sponsors care more about brand association than measurable performance. If you write about a tight niche—like SaaS financial planning or Webflow development—sponsors often pay for access to a specific audience, not raw impressions.

    The risk you carry: If your open rate drops from 45% to 32% because you changed your subject line format or your ESP flagged a spam issue, you still owe the sponsor the same exposure. You absorb that variance.

    One non-obvious move: If you sell flat-rate sponsorships, specify a minimum list size or a floor open rate in your media kit. That way, if your list shrinks unexpectedly, you can renegotiate or pause the deal without breaching the agreement.

    CPM: scales with engagement, requires volume

    CPM pricing charges per thousand opens (or sends, depending on how you define “impression”). If you charge $40 CPM and 6,000 people open the email, the sponsor pays $240.

    When it works: CPM makes sense once your list exceeds 10,000 subscribers and your open rate is stable. Sponsors who buy CPM deals usually run them across multiple newsletters simultaneously and care about cost efficiency at scale. They’re comparing your $40 CPM to someone else’s $55.

    The risk the sponsor carries: If your open rate climbs from 38% to 50% because you tightened your subject lines, the sponsor pays more for the same ad slot. That’s why CPM buyers almost always ask for historical open rate data before committing.

    Pricing benchmark: As of mid-2026, B2B newsletter CPMs range from $30 to $80 depending on niche. Developer tools and finance newsletters command the high end; general business content sits closer to $35–$45.

    Performance-based: aligned incentives, delayed payment

    Performance deals tie payment to outcomes: cost per click (CPC), cost per signup, or cost per sale. The sponsor pays $2 for every click to their landing page, or 20% of revenue from customers who convert within 30 days.

    When it works: Performance pricing works when the sponsor has a tight attribution model and you’re confident your audience acts on recommendations. Affiliate programs and course launches almost always use performance terms. SaaS trials sometimes do, too.

    The risk you carry: You can send to 15,000 people, write a strong callout, and still earn nothing if the sponsor’s landing page loads slowly or their offer doesn’t resonate. You’ve spent the inventory; they’ve paid $0.

    What to negotiate: If you agree to performance pricing, ask for a small flat fee as a baseline—say, $200 guaranteed plus $1.50 per click above 100 clicks. That way, you’re compensated for the list access even if conversion falls short.

    Hybrid models: combining upfront + performance

    Some operators charge a modest flat fee ($300) plus a performance kicker (10% of attributed revenue, or $0.75 per click). This splits risk more evenly and works well when both parties are testing fit for the first time.

    Hybrid deals also make renewals easier. If the performance component delivers, the sponsor comes back and often agrees to raise the flat-rate floor. If it underperforms, you’ve still covered your baseline cost.

    Which model to lead with

    If your list is under 5,000 and growing inconsistently, start with flat rates. Lock in predictable revenue while you stabilize open rates and refine your pitch.

    Once you cross 10,000 subscribers and your monthly open rate variance drops below 5 percentage points, switch to CPM. You’ll earn more as your list grows, and sponsors will find your pricing easier to compare.

    Save performance-based deals for sponsors who already know your audience converts—typically after you’ve run one flat-rate or CPM test with them and the click-through rate exceeded 3%.

    One last note: whichever model you pick, write it into a short sponsorship agreement before you send the invoice. Specify the pricing model, the measurement window, and what happens if you need to reschedule the send. It’s a two-paragraph PDF, and it prevents confusion when the sponsor’s finance team asks why the invoice doesn’t match their internal estimate.

    Want more on running a content business without guessing? Subscribe to One Two Three Send and get one operator-focused article like this in your inbox weekly.

  • Lemon Squeezy’s webhook signature verification: how it works

    Lemon Squeezy’s webhook signature verification: how it works

    Lemon Squeezy's webhook signature verification: how it works
    Photo: Iamovichowdhury via Wikimedia Commons (CC BY-SA 4.0)

    If you’re running a paid newsletter, course platform, or membership site on Lemon Squeezy, you’re probably listening for webhooks to provision access, update subscriber status, or log revenue events. But every webhook Lemon Squeezy sends includes a signature header that most operators ignore—and that’s a problem.

    Webhook signature verification isn’t optional security theater. It’s the only way to confirm that the POST request hitting your server actually came from Lemon Squeezy, not a malicious actor replaying old payloads or inventing fake subscription events.

    Here’s how Lemon Squeezy’s signature system works, how to validate it in your code, and one non-obvious edge case that breaks verification even when you’ve done everything right.

    How Lemon Squeezy generates the signature

    Every webhook Lemon Squeezy sends includes an X-Signature header. This is an HMAC-SHA256 hash of the raw request body, signed with your webhook signing secret.

    You’ll find your signing secret in the Lemon Squeezy dashboard under Settings → Webhooks. It looks like a long alphanumeric string starting with whsec_. Each webhook endpoint you create gets its own secret—if you rotate or delete an endpoint, the secret changes.

    The signature process is straightforward: Lemon Squeezy takes the entire JSON payload, hashes it using HMAC-SHA256 with your secret as the key, then sends the resulting hash in the header. Your server’s job is to recreate that hash and compare it to what was sent.

    Validating the signature in your code

    Here’s a minimal Node.js example using Express and the built-in crypto module:

    const crypto = require('crypto');
    const express = require('express');
    const app = express();
    
    const WEBHOOK_SECRET = process.env.LEMON_SQUEEZY_SECRET;
    
    app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
      const signature = req.headers['x-signature'];
      const payload = req.body;
    
      const hash = crypto
        .createHmac('sha256', WEBHOOK_SECRET)
        .update(payload)
        .digest('hex');
    
      if (hash !== signature) {
        console.error('Invalid signature');
        return res.status(401).send('Unauthorized');
      }
    
      // Signature valid—process the webhook
      const event = JSON.parse(payload);
      console.log('Event type:', event.meta.event_name);
    
      res.status(200).send('OK');
    });
    

    Two things to note: you must use express.raw() to preserve the raw request body. If you parse the JSON first with express.json(), the signature won’t match—whitespace, key order, and encoding all matter. You also need to compare the hash as a hex string, not a buffer.

    If you’re using PHP, the process is similar with hash_hmac(). Python uses hmac.new() from the standard library. The algorithm is the same across languages.

    The edge case: timestamp drift and replay attacks

    Lemon Squeezy doesn’t include a timestamp in the signature itself, which means a valid webhook payload can be replayed indefinitely. If an attacker intercepts a legitimate webhook—say, a subscription cancellation event—they can resend it to your endpoint days or weeks later, and your signature check will pass.

    The mitigation is to track processed webhook IDs. Every Lemon Squeezy webhook includes a unique meta.webhook_id field in the payload. Before processing the event, check whether you’ve already seen that ID. If you have, return a 200 status but skip the business logic.

    Store processed IDs in a database table with a TTL—30 days is reasonable. This prevents replay attacks without requiring you to implement a sliding timestamp window.

    Here’s the addition to the earlier example:

    const event = JSON.parse(payload);
    const webhookId = event.meta.webhook_id;
    
    // Check if already processed (pseudo-code)
    if (await db.webhookExists(webhookId)) {
      console.log('Duplicate webhook, ignoring');
      return res.status(200).send('OK');
    }
    
    await db.saveWebhookId(webhookId);
    // Continue processing...
    

    When verification fails even though it shouldn’t

    If your signature check is failing consistently and you’re confident the secret is correct, check your server’s request body size limit. Some frameworks—Express included—default to a 100kb limit. Lemon Squeezy’s order_created webhooks can exceed that if the order includes multiple line items or custom data fields.

    Increase the limit in your middleware config: express.raw({ type: 'application/json', limit: '1mb' }).

    The other common culprit is proxies or load balancers that rewrite the request body. If you’re behind Cloudflare, Nginx, or AWS ALB, confirm that the raw body is being forwarded unmodified. A single trailing newline or whitespace change will break the hash.

    Lemon Squeezy doesn’t currently offer a signature verification test mode or sample payloads with pre-signed signatures, so the easiest way to debug is to log both the computed hash and the received signature, then compare them character by character.

    If you’re handling payments or provisioning access via webhooks, signature verification isn’t optional. It’s the only way to distinguish legitimate events from forged requests. Set it up once, test it with a live webhook, and add replay protection if your product handles high-value transactions.

    Got a question about webhook security or payment automation? Reply to this email—we cover this stuff every week.