Author: onetwothreeadmin

  • Productivity automation fail states: three ways workflows break silently

    Productivity automation fail states: three ways workflows break silently

    Productivity automation fail states: three ways workflows break silently
    Photo: Lbeaumont via Wikimedia Commons (CC BY-SA 4.0)

    The worst kind of automation failure is the one you don’t notice for three weeks.

    Your workflow dashboard shows green checkmarks. The logs say “success.” But subscribers aren’t getting added to your CRM, payments aren’t triggering welcome emails, and your analytics are missing half the events they should be tracking.

    Silent failures compound. By the time you notice, you’re reconciling weeks of missing data, apologizing to customers, or discovering that your entire attribution model has been off since you changed one field name in a form.

    Here are the three most common fail states that look like successes—and how to catch them before they cost you.

    The webhook returned 200, but nothing processed

    HTTP status codes lie.

    When Zapier, Make, or n8n sends data to your app via webhook, a 200 OK response just means the server acknowledged receipt. It doesn’t mean the data was validated, stored, or acted on.

    I’ve seen this break two ways:

    • The receiving endpoint changed its required fields. Your automation still sends the old structure. The server responds with 200 but silently drops the payload because it failed internal validation.
    • The webhook hits a rate limit or queue. The server accepts it, returns 200, then discards it when the queue overflows five minutes later.

    The fix: log the full response body, not just the status code. If your automation platform supports it, add a conditional step that checks for a specific success token in the response JSON—something like "status": "processed" or "id": "12345". If that field is missing, trigger an alert.

    For high-stakes workflows—payment confirmations, subscriber imports—set up a daily reconciliation check. Compare your source system’s record count to your destination. If they drift by more than a threshold, you know something’s dropping.

    The trigger fired twice, but your deduplication logic failed

    Most automation platforms have built-in deduplication, but it’s not foolproof.

    If a form submission webhook retries due to a network hiccup, or if two browser tabs both fire the same event within milliseconds, you can end up with duplicate actions: two welcome emails, two Slack notifications, two rows in your spreadsheet.

    The problem gets worse when you’re chaining automations. Zapier fires a Make scenario, which posts to n8n, which updates Airtable. Each handoff is another chance for a retry to slip through.

    What breaks deduplication:

    • Time-based IDs. If your deduplication key is a timestamp rounded to the second, two events in the same second collide.
    • Missing IDs entirely. Some tools don’t pass a unique identifier. You’re deduping on email address or name, which fails when the same person submits twice legitimately.
    • Platform memory windows. Zapier’s deduplication only remembers recent tasks. If someone resubmits after a week, it might not catch it.

    The fix: generate your own unique ID at the source. If you control the form or webhook trigger, append a UUID or nanoid to the payload. Use that as your deduplication key, and store it in a simple key-value store (Redis, a dedicated Airtable table, or even a Google Sheet) with a longer TTL than your platform’s memory window.

    The automation ran, but the data format changed midstream

    This is the sneakiest one.

    You built a workflow six months ago that pulls data from your payment processor, reformats it, and logs it to your analytics dashboard. It’s been running perfectly.

    Then your payment processor updates its API. One field—say, customer_email—gets renamed to email. Or a string that used to be "USD" is now returned as {"currency": "USD", "symbol": "$"}.

    Your automation still runs. It doesn’t throw an error. But every row in your analytics dashboard now has a blank email column, or your currency filter breaks because it’s comparing a string to an object.

    You don’t notice until you run a report and realize your MRR tracking has been off for two months.

    The fix: schema validation as a step. Before your automation writes data anywhere important, add a filter or code step that checks the shape of the incoming payload. If a required field is missing or the wrong type, fail loudly—send yourself a Slack message, log an error, halt the workflow.

    Tools like Zapier’s Code step or Make’s JSON validation module let you write a quick function:

    if (!input.customer_email || typeof input.customer_email !== 'string') { throw new Error('Schema mismatch'); }

    For high-volume workflows, consider a monitoring tool like Sentry or a simple daily cron that samples recent records and flags anomalies.

    When to add redundancy vs. when to simplify

    The instinct when you discover silent failures is to add more checks: redundant logs, backup automations, parallel workflows.

    That works for mission-critical paths—payment confirmations, subscriber onboarding. But for everything else, the better fix is often to reduce the number of handoffs.

    If your workflow chains four tools together, each one is a potential fail point. Can you consolidate two steps into one? Can you pull data directly from the source instead of passing it through an intermediary?

    The fewer transformations, the fewer places for silent corruption.

    Want more breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and workflows that actually run online businesses—no fluff, just the mechanics that matter.

  • WordPress REST API authentication: tokens vs. cookies vs. nonces

    WordPress REST API authentication: tokens vs. cookies vs. nonces

    WordPress REST API authentication: tokens vs. cookies vs. nonces
    Photo: Bilal Elmoussaoui and Authenticator contributors via Wikimedia Commons (GPLv3)

    If you’re building custom workflows on top of WordPress—feeding published posts into a social scheduler, syncing custom fields to an external CRM, or triggering email sends when a post goes live—you’re probably using the REST API. And authentication is where most operators hit a wall.

    WordPress offers three main authentication methods for REST API requests: application passwords (tokens), cookie authentication, and nonces. Each behaves differently, and choosing the wrong one means your automation fails silently or exposes your site to unnecessary risk.

    Application passwords: the safe default for external tools

    Application passwords were added to WordPress core in version 5.6. They’re revocable tokens tied to a specific user account, designed for external applications that need programmatic access without exposing your actual login password.

    When you generate an application password from your user profile, WordPress creates a 24-character token. You pass it via HTTP Basic Auth in every API request. If your workflow tool gets compromised or you stop using it, you revoke the token—your main password stays intact.

    This is the right choice for any tool that lives outside WordPress: Zapier workflows, custom Node.js scripts, Python automation, or third-party SaaS that needs to read or write data. The token can’t be used to log into the WordPress admin, only to authenticate API calls.

    Non-obvious gotcha: Application passwords only work over HTTPS. If your staging site uses HTTP, authentication will fail with a vague 401 error. WordPress blocks the feature entirely on non-encrypted connections.

    Cookie authentication: built-in, but brittle for automation

    Cookie authentication is what WordPress uses when you’re logged into the admin and browse the site. Your session cookie proves who you are. The REST API respects that cookie, so any JavaScript running on your own site—inside the WordPress admin or on the front end—can make authenticated requests without extra setup.

    This works fine for plugins or custom admin dashboards. But it’s unreliable for external automation. Cookies expire. They don’t travel well across domains. And if you’re running a headless setup or calling the API from a server, cookies don’t exist.

    Cookie-based requests also require a valid nonce for any write operation (POST, PUT, DELETE). The nonce is a time-limited token WordPress generates to prevent cross-site request forgery. It’s automatically included in admin-area JavaScript via wp_localize_script(), but if you’re building a custom front-end interface, you need to fetch and attach it manually.

    When to use it: Custom admin-area tools, React-based dashboards embedded in WordPress, or AJAX requests from logged-in users on the front end. Not for cron jobs, external services, or anything that runs without an active browser session.

    Nonces: not authentication, just anti-forgery

    Nonces are often confused with authentication, but they’re not. A nonce proves a request came from your site—not from a malicious third-party form. It doesn’t prove who sent the request; it just checks that the request originated from a legitimate WordPress-generated page.

    Nonces expire after 24 hours by default (technically 12–24 hours depending on when they were generated). If your automation fetches a nonce and then waits two days to use it, the request fails.

    You generate a nonce in PHP with wp_create_nonce('action-name') and validate it with wp_verify_nonce(). The REST API checks nonces automatically when you use cookie authentication, but only for destructive operations. Read-only GET requests don’t need one.

    Common mistake: Hardcoding a nonce into a JavaScript file. It expires, and your AJAX calls start failing silently. Always generate nonces dynamically and pass them to your script at page load.

    Which one to use

    If you’re calling the WordPress REST API from outside WordPress—Zapier, a headless front end, a Python script, a mobile app—use application passwords. Generate one per tool, label it clearly, and revoke it when you’re done.

    If you’re building a feature inside WordPress—a custom admin page, a front-end dashboard for logged-in users—use cookie authentication. WordPress handles the session for you. Just remember to include a nonce for write operations.

    If you’re passing data between WordPress and an external service that you control, consider setting up a custom endpoint with a shared secret instead of relying on user-based authentication. Store the secret in an environment variable, check it in your endpoint logic, and skip the user-permission overhead entirely.

    Most authentication failures in WordPress automations come from mixing these methods or assuming cookies work outside the browser. Pick the method that matches where your code runs, and half your API errors disappear.

    Got a WordPress automation question? Reply to this email—we cover one reader question every Sunday.

  • ConvertKit automation delay settings: how long between steps

    ConvertKit’s visual automation builder lets you add delays between steps—wait 3 days, then send an email; wait 1 hour, then tag the subscriber. But the delay feature doesn’t work the way most operators expect, and misunderstanding it can break onboarding sequences, drip campaigns, and time-sensitive offers.

    How ConvertKit measures delay duration

    When you set a delay of “3 days,” ConvertKit waits exactly 72 hours from the moment the subscriber enters that delay step. It’s not “3 business days” or “3 days at 9 a.m.” It’s a rolling 72-hour timer that starts the instant the previous action completes.

    If someone subscribes at 2:37 p.m. on a Tuesday and your automation includes a 3-day delay before the welcome email, that email sends at 2:37 p.m. on Friday. If you have 200 subscribers enter the automation throughout the day, you’ll have 200 different send times spread across the clock.

    This matters most when you’re running time-sensitive promotions or trying to align emails with specific days of the week. A “launch on Monday” automation that uses a 7-day delay from sign-up will send on different days depending on when people joined.

    Delay minimums and processing lag

    ConvertKit’s shortest delay is 1 hour. You can’t set a 15-minute or 5-minute delay. If you need tighter timing—say, sending a lead magnet immediately followed by a second email 10 minutes later—you’ll need to use two separate broadcasts or handle the second email outside ConvertKit.

    There’s also a processing window. ConvertKit doesn’t guarantee that a 1-hour delay fires at exactly 60 minutes. In practice, most delays resolve within a few minutes of the target time, but during high-traffic periods (big launches, Black Friday campaigns), you might see 5–10 minute lag on short delays. Longer delays (24+ hours) tend to be more precise.

    One operator I spoke with runs a 5-day product launch sequence and noticed emails sometimes landed 8–12 minutes late during a coordinated launch with affiliates. For most content sequences, that’s irrelevant. For a flash sale ending at noon, it’s a problem.

    Stacking delays vs. using date-based rules

    If you want emails to send on specific days regardless of sign-up time, don’t use delay steps alone. ConvertKit’s automation builder includes a “Wait until a specific day/time” condition. You can set a rule like “wait until next Wednesday at 10 a.m.” instead of “wait 3 days.”

    This batches subscribers. Everyone who enters the automation between Wednesday at 10:01 a.m. and the following Tuesday at 11:59 p.m. will receive the next email on Wednesday at 10 a.m. It’s cleaner for weekly digest-style sequences or coordinated launches.

    The tradeoff: if someone subscribes on Tuesday, they wait 8 days instead of 7. If they subscribe on Wednesday at 9 a.m., they wait 7 days minus 1 hour. The timing variance shifts from send time to wait duration.

    I use day-based rules for anything tied to external deadlines (webinar reminders, cart-close emails) and rolling delays for evergreen onboarding where the calendar date doesn’t matter.

    One non-obvious trick: buffer delays before conditional splits

    If your automation includes a conditional split—”if they opened the last email, send A; if not, send B”—add a short delay before the condition checks. ConvertKit needs time to register the open event. If you check immediately after sending, most subscribers won’t have opened yet, and your condition will route everyone to the “didn’t open” branch.

    A 6-hour or 12-hour buffer before checking open/click conditions gives the data time to populate. I’ve seen operators skip this and wonder why 95% of their list is routed to the “low engagement” path when open rates are actually 40%+. The timing was the issue, not the engagement.

    ConvertKit doesn’t surface this in the UI. The delay step just says “wait X hours”—it doesn’t explain why you might need it before a condition. But once you know, it’s an easy fix.

    Want more breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools that run online businesses.

  • 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.

  • Reddit’s API pricing killed third-party tools—but RSS still works

    Reddit’s API pricing killed third-party tools—but RSS still works

    Reddit's API pricing killed third-party tools—but RSS still works
    Photo by Brett Jordan on Unsplash

    Reddit’s 2023 API pricing changes forced Apollo, RIF, and a dozen other third-party clients to shut down. The fallout hit solo operators hardest: social listening dashboards, content aggregation tools, and automated posting workflows all broke overnight unless you could justify enterprise-tier API costs.

    Most operators can’t. Reddit’s Developer Platform pricing starts at $0.24 per 1,000 API calls after a 100-call daily free tier. If you’re monitoring a dozen subreddits for content ideas or tracking brand mentions across 50 threads per day, you’ll hit the cap in under a week. Scale to automation or multi-account management, and you’re looking at $12,000+ annually.

    But Reddit’s RSS feeds—an older, quieter feature—still work. They’re unmetered, require no authentication, and return clean XML you can parse with any feed reader or workflow tool. They won’t replace a full API integration, but for content monitoring, trend research, and lightweight automation, RSS covers 80% of what solo operators actually need.

    How Reddit RSS feeds work

    Every subreddit, user profile, and search query has an RSS endpoint. Append .rss to almost any Reddit URL:

    • reddit.com/r/SaaS/.rss — all posts in r/SaaS
    • reddit.com/r/SaaS/new/.rss — sorted by new
    • reddit.com/r/SaaS/search.rss?q=pricing&restrict_sr=1 — search results within a subreddit
    • reddit.com/user/username/.rss — a user’s public post history

    Feeds return the 25 most recent items. No pagination, no historical backfill. Reddit updates feeds every few minutes, but there’s no guaranteed refresh interval. For monitoring breaking discussions, expect a 5–15 minute lag.

    You don’t need an API key. You don’t need OAuth. You don’t need a developer account. Just fetch the URL and parse the XML.

    What you can (and can’t) do with RSS

    RSS works for:

    • Monitoring subreddits for content ideas or competitor mentions
    • Aggregating niche community discussions into a single feed reader
    • Triggering Zapier or Make workflows when keywords appear in post titles
    • Pulling top posts into a weekly digest or Slack channel

    RSS doesn’t give you:

    • Comment threads (only top-level post data)
    • Vote counts, upvote velocity, or ranking signals
    • The ability to post, reply, or interact programmatically
    • More than 25 items per feed

    If you need to analyze engagement metrics, scrape comment sentiment, or automate posting, you’re back to the paid API or manual workflows. But if your goal is awareness—knowing what’s being discussed, when, and by whom—RSS handles it.

    The reliability question

    Reddit hasn’t officially deprecated RSS, but they haven’t promoted it in years. The feature predates the 2023 API lockdown and wasn’t part of the pricing announcement. That makes it both useful and precarious.

    There’s no SLA. No public roadmap. No guarantee Reddit won’t shut it down next quarter to push more traffic through the official app and API tiers. Operators who built RSS-dependent workflows in 2024 are watching for signs: feed downtime, format changes, or silent rate-limiting.

    So far, the feeds are stable. Anecdotally, I’ve seen zero throttling on personal projects polling 40+ subreddit feeds every 15 minutes via Feedly and Zapier. That doesn’t mean Reddit won’t change course, but it does mean the feature isn’t being aggressively sunsetted yet.

    If you’re considering an RSS-based Reddit workflow, build with an exit plan. Use a feed reader or automation tool that lets you swap sources quickly. Don’t hard-code Reddit URLs into customer-facing products. Treat RSS as a time-limited workaround, not infrastructure.

    Practical setup

    The simplest approach: drop Reddit RSS URLs into Feedly, Inoreader, or any standard feed reader. Tag by topic, filter by keyword, and review daily.

    For automation, connect RSS to Zapier, Make, or n8n. Trigger actions when a post title matches a keyword or when a subreddit publishes anything new. Example: new post in r/SaaS containing “launch” → send to Slack → log in Notion.

    If you’re comfortable with code, fetch the .rss endpoint with a standard HTTP client, parse the XML, and filter in your own pipeline. No auth headers, no token refresh, no SDK to maintain.

    Reddit’s RSS feeds aren’t a substitute for real API access, but they’re a functional stopgap while the platform’s developer ecosystem remains priced out of reach for most solo operators. Use them while they last.

    Running a content-driven business? One Two Three Send covers tools, workflows, and operator tactics every week. Subscribe here.

  • 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.

  • WordPress multisite subdomain DNS: how wildcard records actually work

    WordPress multisite in subdomain mode lets you spin up site1.example.com, site2.example.com, and so on—all from a single WordPress install. It’s powerful for niche site portfolios, client networks, or SaaS-style content platforms. But the DNS setup trips up even experienced operators, especially when subdomains don’t resolve or SSL certificates fail to provision.

    Here’s how the wildcard DNS record actually works, what propagation looks like in practice, and the edge cases that break automated SSL issuance.

    What the wildcard A record does

    When you configure WordPress multisite in subdomain mode, you add a single DNS record at your registrar or DNS provider:

    *.example.com A 203.0.113.45

    That asterisk is a wildcard. It tells DNS resolvers: “any subdomain that doesn’t have its own explicit record should point to this IP address.” So blog.example.com, shop.example.com, and anythingyouwant.example.com all resolve to the same server—your WordPress host.

    The WordPress application then inspects the Host header in each HTTP request and serves the correct site from its internal database. The DNS layer doesn’t know or care which subdomains exist; it just routes everything to the same place.

    Propagation timing and the root domain exception

    Wildcard DNS propagates like any other record—typically within minutes to a few hours, depending on TTL and resolver caching. But two gotchas appear frequently:

    The root domain doesn’t match the wildcard. If you have an existing A record for example.com pointing to a different IP (say, a marketing site on a separate host), that takes precedence. The wildcard only catches subdomains. If you want example.com itself to serve a multisite network site, you need a separate A record for the root, and it must point to the same IP as the wildcard.

    Explicit subdomain records override the wildcard. If you previously set up mail.example.com A 198.51.100.10 for an email service, that record wins. The wildcard only applies when no more-specific record exists. Audit your DNS zone file before enabling multisite—old staging subdomains or forgotten services can create confusing “site not found” errors.

    SSL certificate provisioning and wildcard complications

    Most managed WordPress hosts and CDNs (Cloudflare, Kinsta, WP Engine) offer automatic Let’s Encrypt SSL. But wildcard certificates require DNS-01 challenge validation, not the simpler HTTP-01 method.

    Here’s what that means in practice:

    • Single-site certificates use HTTP-01: Let’s Encrypt places a file at example.com/.well-known/acme-challenge/token, retrieves it, and issues the cert. Takes seconds.
    • Wildcard certificates use DNS-01: Let’s Encrypt asks you to create a TXT record at _acme-challenge.example.com, waits for propagation, validates it, then issues. This requires API access to your DNS provider, which not all hosts support automatically.

    If your host doesn’t support wildcard SSL automation, you have two options:

    1. Manually provision wildcard certs every 90 days (painful).
    2. Use a reverse proxy like Cloudflare in front of WordPress, letting Cloudflare handle wildcard SSL termination. Traffic flows: visitor → Cloudflare (SSL) → origin server (can be HTTP or a Cloudflare-issued origin cert).

    Cloudflare’s free tier includes wildcard SSL and works well for multisite operators who don’t need enterprise SLA guarantees. Just ensure SSL/TLS mode is set to “Full” or “Full (strict)”—”Flexible” mode (Cloudflare-to-visitor encrypted, Cloudflare-to-origin unencrypted) creates mixed-content warnings and breaks WordPress admin over HTTPS.

    When new subsites don’t resolve immediately

    You create a new subsite in WordPress, visit newsite.example.com, and get a DNS error. The wildcard’s already in place—what’s wrong?

    Two common causes:

    Local DNS cache. Your machine or router cached a previous NXDOMAIN (non-existent domain) response. Flush your local DNS cache (sudo dscacheutil -flushcache on macOS, ipconfig /flushdns on Windows) or wait 5–15 minutes.

    CAA records blocking SSL issuance. If you have a CAA record at the root domain restricting which certificate authorities can issue certs (e.g., example.com CAA 0 issue "letsencrypt.org"), and your host uses a different CA or expects wildcard issuance, the cert request fails silently. Check your DNS zone for CAA records if SSL won’t provision for new subsites.

    One non-obvious tip: use a staging wildcard on a separate domain

    If you’re testing multisite before going live, don’t use a subdomain of your production domain—use a completely separate domain or a .test suffix with local /etc/hosts entries. Why? Because once you add the wildcard A record to your live domain, every possible subdomain resolves, including ones you haven’t created yet. That can expose staging sites to search engines or curious visitors poking around common subdomain names like staging.example.com or dev.example.com.

    A safer pattern: register example-staging.com, apply the wildcard there, and test your network in isolation. When ready, migrate to the production domain with confidence that DNS and SSL won’t surprise you.

    Got a WordPress multisite setup question? Hit reply—we’d love to feature your scenario in a future Q&A piece.

  • Traffic attribution windows: 1-day vs. 7-day vs. 30-day click

    Traffic attribution windows: 1-day vs. 7-day vs. 30-day click

    Traffic attribution windows: 1-day vs. 7-day vs. 30-day click
    Photo by Frank Rolando Romero on Unsplash

    Attribution windows control how long a click gets credit for a conversion. A visitor clicks your Facebook ad today, signs up tomorrow—does the ad get credit? That depends on your window setting.

    Most platforms default to 7-day click attribution. Google Ads, Facebook Ads Manager, and analytics tools treat this as the standard. But 1-day and 30-day windows exist for good reasons, and picking the wrong one skews your entire acquisition strategy.

    What each window measures

    A 1-day click window credits conversions that happen within 24 hours of the click. It favors high-intent traffic—people who click and convert immediately. If you run retargeting ads or promote time-sensitive offers, 1-day attribution isolates fast movers. It also deflates your reported conversion rate, because anyone who takes two days to decide doesn’t count.

    A 7-day click window stretches the timeline to a week. This captures people who click, bookmark, think it over, then come back and subscribe or buy. Most B2C purchases and newsletter signups fall inside seven days. It’s why this is the default: it balances immediacy with realistic decision cycles.

    A 30-day click window credits conversions up to a month after the click. This favors long consideration cycles—B2B SaaS trials, high-ticket courses, consulting services. If your average customer reads three blog posts and downloads a lead magnet before buying, 30-day windows give you a fuller picture. The downside: they inflate attribution for channels that generate early awareness but don’t close the sale.

    When short windows hide channel value

    If you run cold traffic campaigns—SEO blog posts, YouTube tutorials, LinkedIn thought leadership—1-day windows will make those channels look terrible. Someone discovers your site via Google, reads two posts, subscribes to your newsletter three days later. A 1-day window credits nothing. A 7-day window credits the blog post. A 30-day window credits it plus any other touchpoint in the prior month.

    This is why content marketers and SEO operators prefer longer windows. Content rarely converts same-day. If you judge an SEO article by 1-day attribution, you’ll kill posts that actually seed your funnel.

    Conversely, if you run retargeting ads or flash sales, 7-day and 30-day windows give credit to clicks that didn’t matter. Someone clicked your carousel ad two weeks ago, forgot about it, then found you via organic search and subscribed. The 30-day window credits the ad. The 1-day window credits nothing, because the real driver was search.

    Platform defaults and where they diverge

    Google Ads defaults to 30-day click attribution for conversions. Facebook Ads Manager defaults to 7-day click, 1-day view. Google Analytics 4 lets you set attribution windows per conversion event, but defaults to 90 days for some goals and 30 for others, depending on how you configured them.

    If you’re comparing Facebook CPM to Google Search CPC, and Facebook reports conversions on a 7-day window while Google uses 30, you’re not comparing the same metric. Facebook looks cheaper because fewer conversions qualify. Normalize the windows before you shift budget.

    Stripe and payment processors don’t use attribution windows—they timestamp purchases. If you’re reconciling ad spend to revenue, you need to apply the window logic yourself. Export clicks by date, exports conversions by date, match them within your chosen window, then calculate cost per acquisition. Most operators skip this step and wonder why dashboard revenue doesn’t match bank deposits.

    How to pick your window

    Start with your median time-to-conversion. If 80% of your newsletter subscribers sign up within 48 hours of first visit, a 7-day window is fine. If 60% of your course buyers take two weeks to decide, use 30 days.

    Run a simple query: pull your conversion events (signups, purchases, trial starts) and join them to first-touch timestamps. Calculate the gap. If the 75th percentile is under seven days, a 7-day window won’t lose much signal. If it’s over two weeks, you need 30.

    For operators running multiple channels, set different windows per channel in your spreadsheet or BI tool, even if the platform won’t let you. Tag SEO traffic with a 30-day window, tag retargeting with 1-day, then compare them honestly. You’ll stop over-investing in channels that look good under long windows but don’t actually close.

    One more thing: if you change your attribution window mid-campaign, your before/after metrics aren’t comparable. Conversion rate, CPA, and ROAS all shift when you change the counting rule. Document the switch, split your reporting periods, and don’t blend the data.

    Want more breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools, metrics, and workflows that actually matter for online operators.

  • AI prompt libraries: when saving templates costs more than rewriting

    AI prompt libraries: when saving templates costs more than rewriting

    AI prompt libraries: when saving templates costs more than rewriting
    Photo: Ginny from USA via Wikimedia Commons (CC BY-SA 2.0)

    Most solo operators start saving AI prompts the moment they get a good output. A Notion database here, a text file there, maybe a dedicated prompt-management SaaS tool. The logic is sound: if a prompt worked once, save it and reuse it.

    But six months in, something shifts. You open your prompt library, copy a saved template, paste it into Claude or ChatGPT, and the output is… wrong. Not catastrophically bad, just off. The tone doesn’t match your current voice. The structure assumes a product feature you deprecated. The examples reference a pricing model you changed in March.

    You spend twelve minutes editing the prompt, testing it, and tweaking the output. Writing from scratch would have taken eight.

    The hidden cost of prompt drift

    Prompts aren’t like code snippets. A function that sorts an array will sort an array forever. But a prompt that generated a great welcome email in January 2026 assumes the context, audience, and product state of January 2026.

    When any of those variables change—your positioning tightens, your audience skews more technical, you add a new tier—the saved prompt becomes subtly misaligned. You don’t notice immediately because the output is plausible. It’s only after you ship it, or read it twice, that you realize it doesn’t quite fit.

    The problem compounds when you save dozens of prompts. Each one is a snapshot of a moment in time. Unless you version them, tag them with context, or add timestamps and notes about what was true when you wrote them, you’re maintaining a library of decaying artifacts.

    When prompt libraries actually work

    There are situations where saving prompts makes sense:

    • Highly repetitive, low-context tasks. If you’re generating meta descriptions for product pages with identical structure, a template works. The input variables (product name, key feature) are stable, and the output format never changes.
    • Prompts with complex, non-obvious structure. If you’ve built a multi-step prompt chain with specific XML tags, conditional logic, or output formatting that took an hour to debug, save it. The setup cost is high enough that rewriting isn’t faster.
    • Team handoffs. If you’re delegating a task to a VA or contractor, a saved prompt with usage notes ensures consistency. You’re not optimizing for speed—you’re optimizing for replicability.

    For everything else—blog intros, email rewrites, social captions, brainstorming lists—the overhead of maintaining a library often exceeds the time saved by reusing a prompt.

    What to do instead

    Most operators don’t need a prompt library. They need a prompt framework—a mental model for constructing prompts on the fly.

    Instead of saving fifty variations of “write a LinkedIn post,” save a three-part structure: role + task + constraints. When you need a LinkedIn post, you reconstruct it in fifteen seconds: “You’re a SaaS founder writing for other founders. Write a 150-word LinkedIn post about why we switched from Stripe to Lemon Squeezy. Casual tone, no hashtags.”

    The framework is portable. It adapts to your current context because you’re generating the prompt, not retrieving it.

    If you do save prompts, treat them like code: version them, add comments, and archive anything older than three months unless you’ve actively used it. A prompt you haven’t touched since March is probably not worth keeping.

    The exception: Claude Projects

    If you’re using Claude’s Projects feature, the calculus changes slightly. Projects let you attach context documents—style guides, product specs, audience notes—that persist across chats. That context is reusable without the drift problem, because it’s modular. You update the style guide once, and every prompt in that project inherits the change.

    But even then, the prompts themselves should be ephemeral. The context is what you’re saving, not the exact wording of every request.

    Most solo operators are over-indexed on saving prompts and under-indexed on refining their ability to write them quickly. The goal isn’t a library of perfect prompts. It’s the skill to generate a good-enough prompt in thirty seconds, every time.

    Want more operator tactics like this? Subscribe to One Two Three Send—one article daily, no fluff, no affiliate spam unless the tool actually fits.

    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.

  • Newsletter A/B tests resolve slower than you think—here’s the lag

    Newsletter A/B tests resolve slower than you think—here’s the lag

    Newsletter A/B tests resolve slower than you think—here's the lag
    Photo by Markus Winkler on Unsplash

    You send an A/B test at 9 a.m. By noon, variant B has a 4% higher open rate than A. You declare a winner, stop the test, and send B to the rest of your list.

    You just made a decision on incomplete data—and probably picked the wrong winner.

    Newsletter A/B tests don’t resolve in real time. Most platforms need 24 to 48 hours of data before statistical significance kicks in. Open rates stabilize slowly, click rates lag further, and early leads often reverse as time zones wake up and inbox behavior shifts throughout the day.

    Why early results mislead

    Email opens don’t happen all at once. The first hour skews toward your most engaged subscribers—people who check email immediately, often on mobile. That audience behaves differently from the median subscriber who opens your email six hours later, or the next morning.

    If variant B uses a curiosity-gap subject line (“You won’t believe…”) and variant A is descriptive, B will likely win in the first two hours. Curiosity hooks grab attention fast. But descriptive lines often perform better over 24 hours because they set accurate expectations and attract clicks from readers who actually want the content.

    Click rates take even longer to stabilize. Opens happen within minutes; clicks happen after reading. If your test measures clicks, you need at least 24 hours. If you’re testing send-time optimization or different audience segments, 48 hours is safer.

    ConvertKit and Beehiiv both recommend waiting 24 hours before evaluating A/B test results. MailerLite’s documentation suggests 48 hours for click-based tests. Postmark doesn’t offer built-in A/B testing—it’s designed for transactional mail—but their support team advises the same window when operators run manual split tests using tags.

    Statistical significance isn’t a progress bar

    Most platforms show a confidence percentage or a “statistical significance” badge. That number updates in real time, but it doesn’t mean what you think it does.

    A 95% confidence score after two hours doesn’t guarantee variant B is the true winner. It means that if the current pattern holds, there’s a 95% chance B is better. But the pattern rarely holds. Early openers are not representative of your full list.

    Platforms calculate significance using sample size and effect size. Small lists hit significance faster, but they’re also more vulnerable to noise. If you have 1,000 subscribers and variant B gets 10 extra opens in the first hour, that might push confidence above 90%—but it’s not stable.

    Larger lists take longer to resolve but produce more reliable results. A 50,000-subscriber test might take 36 hours to hit 95% confidence, but when it does, the winner is far more likely to hold.

    The refresh trap

    Refreshing your analytics dashboard every hour doesn’t speed up the test. It increases the odds you’ll stop early and pick a false winner.

    This isn’t unique to newsletters. A/B testing in any channel—landing pages, ad creative, checkout flows—requires patience. But email has a specific temporal curve that makes early data especially unreliable. Inbox providers throttle delivery. Time zones stagger opens. Engagement drops off after 48 hours for most lists, so the meaningful window is narrow.

    If you’re testing subject lines, wait 24 hours. If you’re testing content, layout, or CTAs, wait 48. If your list is under 5,000 subscribers, add another 12 hours—small sample sizes need more time to smooth out variance.

    When to end a test manually

    Sometimes you need to stop early. If one variant has a 60% open rate and the other has 12%, and you’re six hours in with 2,000 opens, the test is over. Catastrophic failure is obvious.

    But if the gap is 23% vs. 27%, or one variant leads by 30 clicks out of 8,000 sends, let it run. Small edges flip constantly in the first 12 hours.

    Set your test duration when you launch it, then ignore the dashboard until the timer runs out. Most platforms let you configure this in advance—ConvertKit and Beehiiv both allow you to set a fixed test window and auto-send the winner after X hours. Use that feature. It removes the temptation to call it early.

    Want sharper sends? Reply with the A/B test you’re running this week—I’ll tell you if you’re measuring the right thing.

    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.