Author: onetwothreeadmin

  • ConvertKit broadcast send-time personalisation: how it decides

    ConvertKit broadcast send-time personalisation: how it decides

    ConvertKit broadcast send-time personalisation: how it decides
    Photo by Kit (formerly ConvertKit) on Unsplash

    ConvertKit’s broadcast scheduler includes a checkbox labelled “Optimise send time.” When enabled, the platform doesn’t deliver your email at the moment you hit publish—it queues each subscriber’s copy for a window it thinks they’re most likely to engage.

    The feature sounds useful. In practice, it works well for some operators and creates confusion for others. Here’s what actually happens under the hood, when the algorithm helps, and when you’re better off choosing a fixed send time.

    What the algorithm looks at

    ConvertKit’s send-time optimisation scans each subscriber’s engagement history: opens, clicks, and the timestamps associated with both. It looks for patterns—does this person consistently open emails around 7 a.m. Eastern? Do they engage more on weekday mornings or weekend afternoons?

    If the platform identifies a statistically significant pattern, it schedules delivery within a window it predicts will perform better than a one-size-fits-all broadcast time. If no pattern exists—new subscribers, inactive readers, or those with erratic habits—it defaults to your account’s standard send time or the time you manually set when creating the broadcast.

    The optimisation window spans roughly 24 hours. ConvertKit won’t hold a broadcast for days, but it will stagger delivery across morning, afternoon, and evening slots depending on subscriber behaviour.

    When it helps

    Send-time optimisation works best when you have a large, engaged list with diverse time zones and consumption habits. If you’re sending to 10,000 subscribers spread across North America, Europe, and Asia, the feature can lift open rates by 2–8% compared to a single fixed time.

    It’s also useful when your content isn’t time-sensitive. Evergreen tutorials, weekly roundups, and educational sequences don’t lose value if they arrive six hours later than your publish click. The algorithm prioritises engagement over synchronicity.

    Operators who publish frequently—three or more broadcasts per week—also see better results. The platform accumulates more engagement data, which sharpens its predictions. If you send once a month, there’s less signal to work with.

    When manual scheduling wins

    Time-sensitive broadcasts break the optimisation logic. If you’re announcing a product launch, a limited-time discount, or commentary tied to a news event, you want simultaneous delivery. Staggering emails across 24 hours means some subscribers see the offer after it’s expired or the news cycle has moved on.

    Small lists—under 1,000 subscribers—don’t benefit much either. The algorithm needs volume to identify statistically meaningful patterns. With a few hundred people, manual scheduling based on your own audience knowledge often outperforms the automated approach.

    And if your list skews heavily toward a single time zone or demographic, the optimisation adds complexity without much upside. A newsletter serving U.S. East Coast professionals during weekday work hours doesn’t need personalised delivery windows—everyone’s already in the same behaviour bucket.

    One non-obvious tip

    ConvertKit’s send-time optimisation relies on opens as its primary engagement signal, but open tracking has degraded since Apple’s Mail Privacy Protection rolled out in 2021. A meaningful percentage of your subscribers now register artificial opens the moment an email hits their inbox, regardless of when they actually read it.

    This skews the algorithm’s predictions. If you notice erratic or counterintuitive delivery patterns—emails going out at odd hours, open rates drifting downward despite optimisation being enabled—try disabling the feature for two or three broadcasts and compare performance. Manual scheduling at a consistent, tested time often performs better when open-tracking reliability is compromised.

    You can also cross-reference your ConvertKit open data with click data, which remains accurate. If the platform says a subscriber opens at 6 a.m. but consistently clicks at noon, the optimisation may be misfiring. In that case, fall back to manual control.

    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.

  • SEO title tags longer than 60 characters: what Google actually shows

    SEO title tags longer than 60 characters: what Google actually shows

    SEO title tags longer than 60 characters: what Google actually shows
    Photo by Growtika on Unsplash

    Google’s title tag rewriting has gotten aggressive. Even when you write a clean, keyword-focused title, there’s no guarantee that’s what shows up in search results—especially if your title runs long.

    The old rule was simple: keep titles under 60 characters and you’re safe. That’s no longer true. Google now rewrites titles based on query context, page content, and its own interpretation of relevance. But character count still matters, and understanding where the cutoff happens—and what Google does when it hits that limit—can save you from losing clicks to ellipses or mangled rewrites.

    The 60-character guideline is a pixel width estimate, not a hard limit

    Google doesn’t count characters. It measures pixel width. A title with narrow letters like “i” and “l” can stretch past 70 characters and still display fully. A title packed with wide characters like “W” and “M” might get cut at 55.

    The commonly cited 60-character limit assumes an average character width of about 10 pixels, with a total display width around 600 pixels on desktop. Mobile widths are narrower—closer to 78 characters at roughly 920 pixels, but that varies by device and font rendering.

    Practically: if your title is under 60 characters, it’ll almost always display in full. Between 60 and 70, you’re gambling. Past 70, expect truncation or a rewrite.

    When Google truncates vs. when it rewrites entirely

    Truncation is the clean outcome. Your title gets cut mid-sentence and an ellipsis (…) appears. You lose the tail, but the front still represents what you wrote.

    Rewriting is messier. Google pulls text from your H1, meta description, or prominent on-page copy and generates a new title that may not match your intended keyword targeting at all. This happens more often when:

    • Your title is vague or keyword-stuffed
    • Your title doesn’t match the user’s query well
    • Your page has a strong H1 that differs significantly from the title tag
    • Your title exceeds roughly 70 characters and Google decides truncation would be unhelpful

    There’s no deterministic trigger. In testing across a few dozen client sites in early 2026, titles longer than 65 characters were rewritten about 40% of the time, while titles under 55 characters were rewritten less than 10% of the time—but only when the title and H1 were closely aligned.

    How to test what actually appears before you hit publish

    Don’t rely on SERP preview tools in your SEO plugin. They estimate based on character count, not actual Google rendering.

    The most reliable method: publish the page, wait 24–48 hours for indexing, then search for site:yourdomain.com "exact phrase from title" and see what Google shows. If it’s truncated or rewritten, you know before any traffic arrives.

    For faster iteration, use Google’s Rich Results Test or the URL Inspection tool in Search Console. Neither shows the exact SERP title, but they confirm whether your title tag is being read correctly and flag any HTML issues that might trigger a rewrite.

    If you’re split-testing title length, track CTR by title length cohort in Search Console. Filter by page, compare average position to CTR, and look for drop-offs where longer titles perform worse despite similar rankings. That’s your signal to trim.

    The non-obvious move: write for truncation, not against it

    Most operators try to stay under 60 characters to avoid truncation entirely. That’s safe, but it leaves opportunity on the table.

    If you front-load your title with the core keyword and value proposition in the first 50 characters, you can extend the tail with secondary keywords or modifiers that add context for Google’s algorithm—even if users never see them in the SERP.

    Example:
    Short, safe version (52 characters):
    “WordPress CDN setup: Cloudflare configuration guide”

    Extended version (68 characters):
    “WordPress CDN setup: Cloudflare configuration guide for speed & caching”

    The second version might get truncated to “WordPress CDN setup: Cloudflare configuration guide for spe…” in search results, but Google still indexes “speed” and “caching” as relevance signals. You get the ranking boost without sacrificing clarity in the visible title.

    This works best for informational content where secondary keywords reinforce the main topic. It’s riskier for commercial pages where every word in the SERP title needs to convert.

    Want more tactical breakdowns like this? Hit reply and tell us which part of your stack is giving you the most trouble right now—we’ll cover it in an upcoming issue.

  • Sponsored newsletter placements: actual CPM ranges by niche in 2026

    Sponsored newsletter placements: actual CPM ranges by niche in 2026

    Sponsored newsletter placements: actual CPM ranges by niche in 2026
    Photo by mauRÍCIO SANTOS on Unsplash

    If you’re thinking about selling sponsorships in your newsletter, the first question is always: what should I charge?

    Most advice points to CPM—cost per thousand opens—but the ranges you’ll find online are either outdated or so wide they’re useless. Here’s what solo operators and small teams are actually charging in 2026, broken down by niche, list size, and placement type.

    Tech and developer newsletters: $30–$80 CPM

    Tech newsletters with engaged audiences—especially those covering specific tools, frameworks, or workflow topics—command some of the highest rates. A 5,000-subscriber list with a 40% open rate (2,000 opens) can charge $60–$160 for a primary placement.

    The upper end applies when your audience skews toward decision-makers: CTOs, senior engineers, product leads. If your readers are junior devs or students, expect $30–$50 CPM.

    Sponsors in this niche are typically SaaS tools, API platforms, or developer-focused services. They’re buying access to builders who have budget authority or influence over stack decisions.

    Finance, investing, and business: $40–$100 CPM

    Newsletters covering personal finance, investing strategies, or small-business operations sit in a similar range—or higher if the audience is verified high-net-worth.

    A business newsletter with 10,000 opens can charge $400–$1,000 per placement. Sponsors here include fintech apps, investment platforms, business insurance, and accounting tools.

    The key variable is audience income and intent. If your readers are actively managing portfolios or running businesses, CPMs climb. If they’re browsing general money tips, rates drop toward $25–$40.

    Marketing and creator tools: $25–$60 CPM

    Newsletters for marketers, solo operators, and content creators—like this one—typically see $25–$60 CPM. Sponsors are often other newsletters, SaaS tools, courses, or affiliate offers.

    A list with 8,000 opens might charge $200–$480 for a dedicated sponsor block. Secondary placements (classified-style listings or brief mentions) go for $15–$30 CPM.

    This niche is crowded, and many readers are also operators who know what sponsorships cost. That keeps rates honest but also means you need strong engagement metrics—open rates above 35% and click-through above 2%—to justify premium pricing.

    Lifestyle, wellness, and general interest: $15–$40 CPM

    Broader lifestyle newsletters—covering travel, productivity, wellness, or general personal development—sit at the lower end. CPMs range from $15 to $40 depending on engagement and audience demographics.

    A 15,000-open newsletter in this category might charge $225–$600 per sponsor. The challenge is that sponsors often want very specific audience segments (parents, remote workers, specific age ranges), and general-interest lists don’t always deliver that precision.

    Higher rates come from tight positioning. A newsletter for remote-working parents will out-earn a general productivity newsletter with the same open count.

    What actually moves the rate

    Niche matters, but three other factors determine what you can charge:

    • Engagement rate: Opens above 40% and clicks above 3% let you charge 20–30% more than average.
    • Audience intent: Readers who buy tools, subscribe to services, or make business decisions are worth more than passive readers.
    • Sponsor fit: A tight match between your content and the sponsor’s product doubles conversion and justifies premium pricing.

    If you’re just starting, aim for the lower end of your niche’s range. Once you have case studies showing sponsor ROI—real click-through and conversion data—you can move toward the top quartile.

    Where to list your availability

    Most solo operators start by mentioning availability in their newsletter footer or a dedicated sponsor page on their site. Tools like Swapstack, Paved, and SparkLoop’s ad network can connect you with sponsors, though they typically take 20–30% of the placement fee.

    Direct outreach to brands you already use or cover often yields better rates and longer-term relationships. Send a one-page media kit with your open rate, audience breakdown, and two pricing tiers: primary placement and secondary mention.

    If your newsletter runs on Beehiiv, their built-in ad network can match you with sponsors automatically once you hit 500+ subscribers, though CPMs through the network tend to sit at the lower end of these ranges.

    Want more data on what’s working in newsletter monetisation? Reply with your niche and list size—I’ll share what operators in similar categories are seeing.

    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.

  • WordPress multisite subdomain DNS: how wildcard A records actually work

    WordPress multisite subdomain DNS: how wildcard A records actually work

    WordPress multisite subdomain DNS: how wildcard A records actually work
    Photo by Markus Spiske on Unsplash

    WordPress multisite with subdomains sounds elegant in theory: every new site gets sitename.yourdomain.com instead of yourdomain.com/sitename. But the DNS configuration required to make that work trips up even experienced operators, especially when a CDN sits in front.

    If you’ve ever added a new multisite subsite and found it unreachable for hours—or had some subsites resolve while others don’t—the issue is almost always in how your wildcard A record is configured, cached, or overridden.

    What wildcard DNS actually does

    A wildcard A record in your DNS zone looks like this:

    *.yourdomain.com. A 192.0.2.10

    That asterisk tells the nameserver: “for any subdomain that doesn’t have an explicit record, send traffic to this IP.” So when someone visits newsite.yourdomain.com, and you haven’t manually created a DNS entry for newsite, the wildcard catches it and routes it to your server.

    WordPress multisite then inspects the HTTP Host header, matches it to a subsite in the database, and serves the right content. The wildcard doesn’t create the site—it just ensures the request reaches your server.

    But here’s the catch: wildcard records have lower priority than explicit records. If you’ve previously created an A record for staging.yourdomain.com pointing elsewhere, the wildcard won’t override it. That explicit record wins.

    When propagation stalls and caching hides the issue

    DNS records have a Time-To-Live (TTL) value, usually between 300 seconds (5 minutes) and 86400 seconds (24 hours). When you add or change a wildcard A record, recursive resolvers—like those run by ISPs or Cloudflare’s 1.1.1.1—cache the old state until the TTL expires.

    If your previous wildcard record had a 24-hour TTL and you just changed the IP, some visitors won’t see the update for a full day. Your local machine might resolve correctly (because your DNS client happened to refresh), but your phone on cellular data still hits the old IP.

    This creates a debugging mirage: the site works for you, fails for others, and you can’t reproduce the issue. The fix is to lower your wildcard A record TTL to 300 seconds before making changes, wait for the old TTL to expire, then make the change. After it propagates, you can raise the TTL again if you want.

    The CDN wildcard gotcha

    If you’re using a CDN like Cloudflare, BunnyCDN, or Fastly, the wildcard DNS record alone isn’t enough. Most CDNs require you to explicitly enable wildcard proxying or add each subdomain to a whitelist.

    On Cloudflare, for example, wildcard DNS records (*.yourdomain.com) can be proxied (orange cloud), but only on Business or Enterprise plans. On Free and Pro plans, the wildcard record must be DNS-only (grey cloud). That means requests hit your origin server directly—no CDN caching, no DDoS protection, no automatic SSL for new subsites.

    If you’re on a CDN plan that doesn’t support wildcard proxying, you have two options:

    • Upgrade the CDN plan to enable wildcard SSL and proxying.
    • Pre-create DNS records for each subsite manually and proxy those individually. This works, but it defeats the automation benefit of multisite subdomains.

    BunnyCDN supports wildcard pull zones on all plans, but you need to configure the hostname pattern in the pull zone settings. If you skip that step, the CDN won’t know which origin to pull from, and requests fail with a 404 at the edge.

    Non-obvious tip: test with dig, not a browser

    When you’re debugging wildcard DNS, don’t rely on your browser. Browsers cache DNS internally, and your OS has its own resolver cache. Instead, use dig or nslookup to query the authoritative nameserver directly:

    dig @ns1.yourprovider.com randomsubdomain.yourdomain.com

    Replace ns1.yourprovider.com with one of your domain’s authoritative nameservers (find them with dig yourdomain.com NS). If the wildcard is configured correctly, you’ll see an A record response with your origin IP, even for a subdomain that doesn’t exist in WordPress yet.

    If dig returns NXDOMAIN, the wildcard isn’t live yet—either it hasn’t propagated, or it wasn’t saved correctly in your DNS provider’s dashboard.

    When to use multisite subdomains vs. subdirectories

    Subdomain multisite makes sense when you’re running distinct properties under one WordPress install—think a network of regional sites, or client sites you manage centrally. But if you’re a solo operator running one content brand, subdirectories (yourdomain.com/blog, yourdomain.com/shop) are simpler. You avoid DNS complexity, CDN wildcard restrictions, and SSL certificate headaches.

    Subdomain multisite also complicates migration. Moving to a new host means updating the wildcard A record and waiting for propagation. With subdirectories, you update one A record and you’re done.

    If you’re already committed to subdomain multisite and hitting DNS or CDN issues, the fix is usually one of three things: lower TTL, enable wildcard proxying on your CDN, or confirm the wildcard A record actually saved. Test with dig, not guesswork.

    Got a WordPress hosting or DNS question? Reply to this email—I cover one reader question every Sunday.

  • ChatGPT’s Custom Instructions feature: what it actually remembers

    ChatGPT’s Custom Instructions feature: what it actually remembers

    ChatGPT's Custom Instructions feature: what it actually remembers
    Photo by Andrew Neel on Unsplash

    ChatGPT’s Custom Instructions feature lets you define persistent context that carries across every new chat. For solo operators running content calendars, client briefs, or repetitive workflows, it’s supposed to eliminate the copy-paste ritual of re-stating your role, audience, and formatting preferences every single time.

    In practice, it works—but only within specific boundaries that aren’t surfaced in the UI. Understanding what the feature actually retains, when it gets overridden, and how token allocation works will determine whether it saves you time or quietly undermines your prompts.

    What Custom Instructions stores

    The feature splits into two text boxes: “What would you like ChatGPT to know about you?” and “How would you like ChatGPT to respond?” Each accepts up to 1,500 characters. That’s roughly 300–400 tokens, depending on vocabulary.

    The first box is for context: your role, business model, audience, constraints. The second is for output preferences: tone, structure, length, formatting rules.

    Both get prepended to every new conversation as invisible system-level instructions. They don’t appear in the chat transcript, but they consume part of the context window before your first user message even loads.

    This matters because ChatGPT-4’s context window is 8,192 tokens (standard) or 32,768 tokens (extended, via API or Plus with longer chats enabled). If your Custom Instructions use 400 tokens and your conversation history fills another 6,000, you’ve got roughly 1,800 tokens left for the next user prompt and model response combined. Long conversations will eventually push Custom Instructions out of active memory—the model still “sees” them in the system prompt, but prioritises recent turns.

    When instructions get ignored

    Custom Instructions apply to new chats only. If you edit them mid-conversation, the changes don’t retroactively alter the existing thread. Start a fresh chat to pick up the edits.

    They also don’t override explicit contradictions in your user prompt. If your Custom Instructions say “always respond in bullet points” but your message says “write this as a paragraph,” the user prompt wins. The model treats Custom Instructions as defaults, not mandates.

    This is useful when you need to temporarily deviate—run a one-off analysis in a different format—but it also means vague user prompts can dilute or ignore your standing instructions entirely. Specificity in the moment beats standing context.

    Finally, Custom Instructions don’t persist across different ChatGPT interfaces. They apply to the web UI and the iOS/Android apps, but not to API calls, plugins, or third-party wrappers. If you’re running ChatGPT via Zapier, Make, or a custom script, you’ll need to inject that context manually in each request.

    What to put in—and what to skip

    Effective Custom Instructions are narrow and structural, not aspirational. “I run a weekly newsletter about SaaS pricing for B2B founders” is useful. “I value creativity and outside-the-box thinking” is not—it’s too abstract to influence output in a measurable way.

    Good candidates for the context box:

    • Your primary business model and audience (e.g., “solo operator running a paid Substack on AI regulation”)
    • Constraints you apply consistently (e.g., “posts are 800 words, American English, no listicles”)
    • Tools or platforms you use regularly (e.g., “I use ConvertKit and WordPress, not Mailchimp or Wix”)
    • Terminology preferences (e.g., “call them ‘subscribers,’ not ‘users’”)

    Good candidates for the response box:

    • Structural defaults (e.g., “use H2 subheadings, no H3s”)
    • Tone boundaries (e.g., “conversational but not casual, no exclamation marks”)
    • Output length (e.g., “default to 600–800 words unless I specify otherwise”)
    • Formatting rules (e.g., “return HTML, not Markdown”)

    Skip anything that changes project-to-project. Don’t embed client names, specific article topics, or one-off formatting requests—those belong in the user prompt, not standing instructions.

    One non-obvious tip: version your instructions

    Custom Instructions have no built-in versioning or change log. If you tweak them and output quality shifts, you won’t have a record of what changed unless you save snapshots externally.

    Keep a simple text file or note with dated versions of your instructions. When you experiment—tightening tone, adding a structural rule, removing a constraint—log the edit and the date. If output degrades or drifts after a few weeks, you can diff versions and pinpoint what shifted.

    This is especially useful if you’re running ChatGPT in parallel with Claude or another model. Custom Instructions are ChatGPT-specific, but the principles transfer. A versioned reference file lets you port tested context patterns across tools without starting from scratch each time.

    If you’re using Custom Instructions already, reply and tell us what’s in yours—or what you’ve tried and removed. We’re cataloging what actually works for operators running content businesses, not just what the feature says it does.

    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.

  • Analytics pixel load order changes what conversion data you see

    Analytics pixel load order changes what conversion data you see

    Analytics pixel load order changes what conversion data you see

    Most solo operators install analytics pixels the same way: copy the snippet, paste it into a header tag manager or WordPress plugin, and move on. But the order those scripts execute changes which events get tracked, how sessions are attributed, and whether conversions appear at all.

    Load order isn’t a hosting quirk—it’s a silent filter on your data. When a payment confirmation fires before your analytics library finishes initializing, that conversion never gets logged. When a UTM parameter gets stripped by a redirect before your tracking pixel reads it, attribution breaks. And because these failures happen in milliseconds, you won’t notice until you’re reconciling Stripe revenue against reported conversions and the numbers don’t match.

    What happens when pixels fire out of sequence

    Most analytics platforms—Google Analytics 4, Plausible, Fathom, Mixpanel—need their base library loaded before any event tracking calls execute. If a custom event fires while the library is still downloading, it gets dropped. No error. No retry. The event just doesn’t exist in your dashboard.

    This happens most often on single-page apps, checkout flows, and any page where a conversion event fires on page load rather than user action. A typical failure case: you use a Zapier webhook to log a new subscriber in your CRM, and that webhook triggers a thank-you page redirect. If the redirect happens before GA4’s gtag('event', 'conversion') call completes, the conversion never reaches Google’s servers.

    Even when the base library loads in time, the order pixels fire relative to each other determines what data each one sees. If a Facebook Pixel fires before a UTM-stripping redirect, it captures source attribution. If it fires after, it logs the visit as direct traffic. Neither setup is “wrong”—but one gives you attribution data you can act on, and the other doesn’t.

    Where load order gets decided

    If you’re using a tag manager—Google Tag Manager, Matomo Tag Manager, or a WordPress plugin like Site Kit—load order is controlled by trigger rules and priority settings. Most operators leave these at default, which means tags fire in the sequence they were added. That’s fine until you add a new pixel six months later and it fires last, missing fast-loading events.

    GTM lets you set a “tag firing priority” number. Higher numbers fire first. If your GA4 base tag has priority 10 and a custom conversion event has priority 5, the event will try to fire before the library exists. Set the base tag to a higher number than any dependent event, and the problem goes away.

    In WordPress, most analytics plugins inject scripts into wp_head or wp_footer hooks. The order depends on plugin load sequence, which is alphabetical by folder name unless you force a specific order with a plugin like Code Snippets or a custom functions.php entry. If two plugins both hook into wp_head with default priority (10), the one whose folder name comes first alphabetically loads first.

    That’s why renaming a plugin folder or switching from “Google Analytics for WordPress” to “Site Kit by Google” can silently change your tracking setup.

    What breaks when checkout flows redirect too fast

    Payment processors and course platforms almost always redirect immediately after a transaction. Stripe Checkout, Gumroad, and most hosted course tools send the buyer to a confirmation URL within milliseconds of payment. If your analytics pixel hasn’t finished sending the conversion event by the time that redirect fires, the event is abandoned mid-request.

    The fix is to delay the redirect until tracking calls return a success callback—but most platforms don’t expose that hook. Stripe Checkout’s success_url is a hard redirect; there’s no “wait for analytics” option. The workaround is to fire the conversion event on the confirmation page itself, not the checkout page. That way, even if the redirect is instant, the event fires after the user lands on a stable URL.

    This also means your conversion tracking needs to deduplicate. If someone refreshes the thank-you page, the event fires again. GA4 handles this with a client ID and session timestamp; other tools require a transaction ID parameter you pass through the URL and check against a cookie or localStorage flag.

    How to audit what’s actually firing

    Open your browser’s network tab, filter by “analytics,” “gtag,” “pixel,” or the domain of your tracking provider, and reload the page. Watch the order requests appear. If a conversion event request fires before the base library’s analytics.js or gtag.js, you’ve found the problem.

    For checkout flows, test with network throttling enabled (Chrome DevTools lets you simulate slow 3G). If your conversion event only fires on fast connections, it’s a timing race you’ll lose on mobile networks.

    If you’re using Postmark or another transactional email service to send purchase confirmations, compare the timestamp of the email sent against the timestamp of the conversion event in your analytics dashboard. If the email consistently arrives before the event logs, your tracking is firing too late—or not at all.

    Want more operator-to-operator breakdowns of what actually works? Subscribe to One Two Three Send—one article daily, no fluff, no affiliate spam unless the tool genuinely fits the topic.

  • Zapier filter conditions fail when field types mismatch

    Zapier filter conditions fail when field types mismatch

    Zapier filter conditions fail when field types mismatch
    Photo by Patrick Martin on Unsplash

    Zapier filters are supposed to be simple: if this field equals that value, continue. If not, stop. But filters fail more often than you’d expect—not because your logic is wrong, but because the data types don’t match what Zapier expects.

    A subscriber count from your email platform might arrive as text instead of a number. An empty field might be null, an empty string, or the word “null.” Your filter sees the mismatch and silently stops the Zap. No error email. No log entry flagged as critical. Just… nothing.

    Here’s how to catch these failures before they cost you hours of debugging.

    How Zapier evaluates filter conditions

    Zapier’s filter step compares two values: the field from your trigger or previous action, and the reference value you type in. The comparison operator—equals, contains, greater than—determines how Zapier interprets both sides.

    When you choose “equals,” Zapier performs a string comparison by default. If your trigger sends a number but the comparison field expects text, the filter might pass or fail unpredictably depending on how the source app formats the data.

    Example: your email platform sends subscriber count as 1250 (integer). You filter for “greater than 1000” (text). Zapier converts both to strings and compares them lexicographically. "1250" is not greater than "1000" in string comparison—it’s less than "2". The filter fails, and your Zap stops.

    This happens most often with:

    • Numeric fields sent as strings (subscriber counts, order totals, timestamps)
    • Boolean fields sent as "true" or 1 instead of true/false
    • Empty fields that arrive as null, "", "null", or "N/A"
    • Date fields formatted inconsistently across apps

    Diagnosing type mismatches in Zapier’s task history

    When a Zap stops unexpectedly, open the task history and expand the filter step. Zapier shows you the exact values it compared, but it doesn’t label their types.

    Look for these red flags:

    • Quoted numbers: "42" instead of 42
    • Inconsistent empty values: sometimes blank, sometimes "null"
    • Boolean values as strings: "true" instead of true

    If you see quotes around a value that should be numeric, the source app sent it as text. If an empty field shows "" in one run and nothing in another, the app’s API is inconsistent.

    Zapier’s task history keeps logs for 14 days on paid plans (7 days on free). If you’re debugging intermittent filter failures, screenshot the mismatched values before they expire.

    Fixing type coercion with formatter steps

    The fix is to normalise field types before the filter step. Add a Formatter by Zapier action between your trigger and filter, then choose the Transform operation that matches your data:

    For numeric comparisons: Use “Numbers > Format Number” to convert text to integer or decimal. Set the input to your dynamic field, leave the format as “plain number,” and pass the output to your filter.

    For boolean checks: Use “Text > Default Value” to replace empty or inconsistent fields with a standard fallback. If your trigger sometimes sends "true", sometimes 1, and sometimes nothing, map it to "yes" or "no" explicitly.

    For empty-field handling: Use “Text > Default Value” again. Set your dynamic field as input, and specify a fallback like "none" or 0. This ensures your filter always compares against a known value, not an unpredictable empty state.

    For date comparisons: Use “Date/Time > Format” to standardise timestamps into ISO 8601 or Unix epoch format. Then compare them as numbers (epoch seconds) instead of strings.

    Each formatter step costs one task. For high-volume Zaps, that adds up—but it’s cheaper than manually re-running failed tasks or missing critical automations.

    When to use Paths instead of Filters

    If your filter logic has more than two branches—e.g., if subscriber count is under 500, do A; if 500–2000, do B; if over 2000, do C—use Zapier’s Paths feature instead of stacking multiple filters.

    Paths let you define multiple conditions in a single step, each with its own downstream actions. They’re clearer to debug, and they don’t silently stop your Zap if one branch doesn’t match—they just skip that path and try the next one.

    Paths are available on Zapier’s Professional plan and above ($49/month as of mid-2026). If you’re on a lower tier and need branching logic, you’ll need to chain Zaps with webhooks or upgrade.

    Test with edge cases, not happy-path data

    Most operators test Zaps with clean, complete sample data. That’s why type mismatches don’t surface until production.

    Before you turn a Zap on, manually trigger it with:

    • An empty field where you expect a number
    • A zero or negative value where you expect positive
    • A malformed date (e.g., "2026-13-01")
    • A boolean field set to an unexpected value ("false" as text, not false)

    Zapier’s test step only runs with the most recent trigger data. If that data is clean, your test passes—but your production Zap still breaks when messy data arrives.

    Want more debugging tactics for no-code workflows? Reply with the automation tool you use most—I’ll cover its failure modes in a future issue.

  • Stop hoarding traffic sources—concentrate on two

    Stop hoarding traffic sources—concentrate on two

    Stop hoarding traffic sources—concentrate on two
    Photo: Spatms via Wikimedia Commons (CC BY-SA 4.0)

    The typical solo operator’s traffic mix looks like this: a little organic social, some SEO, sporadic guest posts, an underfunded Facebook ad campaign, a LinkedIn presence that gets updated twice a month, and maybe a Reddit comment here and there.

    The result? Marginal traction everywhere and momentum nowhere.

    If you’re running a content-driven business alone or with a tiny team, you don’t have the bandwidth to maintain six traffic channels. You barely have enough to run two well. And running two well will deliver more than running six poorly.

    Why diversification backfires for solo operators

    Diversification makes sense when you have a team. A social manager can own Twitter and LinkedIn. An SEO lead can build out a content calendar. A paid strategist can optimize ad spend.

    When you’re alone, diversification becomes dilution.

    Each traffic channel has its own learning curve, content format, posting cadence, and algorithmic quirks. SEO requires consistent publishing, keyword research, and backlink outreach. Twitter needs daily engagement. Paid ads need creative testing and conversion tracking. LinkedIn wants long-form native posts, not links.

    Switching between them incurs a cognitive tax. You’re not just creating content—you’re context-switching between platforms that reward different behaviors. That overhead kills the compounding effect that makes any single channel work.

    Traffic doesn’t compound. But channel-specific skill does. The more reps you get on one platform, the faster you learn what works, the better your content performs, and the less time each piece takes to produce.

    Pick two, not six

    Here’s the framework: choose one owned channel and one discovery channel.

    Owned: SEO, email, or your own site. Something you control, that builds equity over time, and that doesn’t disappear if an algorithm shifts. For most operators, this is SEO—organic search traffic that arrives because you published something useful and Google indexed it.

    Discovery: Social media, paid ads, communities, or guest platforms. Somewhere people find you before they know you exist. This is where new readers come from. For most, it’s one social platform or a narrow paid strategy.

    If you’re writing a weekly newsletter and trying to grow it, your mix might be: SEO + Twitter. You publish articles on your site, optimize them for search, and share insights and threads on Twitter that link back. Both feed your email list.

    Or: SEO + LinkedIn. You write long-form posts natively on LinkedIn to build authority, and publish evergreen how-tos on your own site that rank and convert.

    Or: Email + Reddit. You don’t have a blog. You participate in niche subreddits, answer questions with real value, and your profile links to your newsletter signup.

    The pair matters less than the commitment. Two channels, both run well, will outperform six channels run inconsistently.

    What “run well” actually means

    Running a channel well means you:

    • Publish or engage on a predictable cadence
    • Track what works and do more of it
    • Understand the platform’s content format and native behavior
    • Spend enough time to see compounding return—usually 3–6 months minimum

    For SEO, that’s 2–4 articles a month, keyword-targeted, with internal links and a basic backlink strategy. For Twitter, it’s daily replies, weekly threads, and a clear niche. For LinkedIn, it’s 2–3 long posts a week that add value without asking for anything.

    If you can’t commit to that cadence on a channel, cut it.

    When to add a third

    Only after one of your two is working without you.

    That usually means you’ve automated part of it (SEO content is ranking and driving passive traffic), hired it out (someone else schedules and monitors your social), or built enough momentum that it runs on reduced effort (your Twitter replies generate inbound DMs even when you take a week off).

    Until then, adding a third channel just splits your attention and stalls progress on the two that matter.

    If you’re spread across six traffic sources right now, audit the last 90 days. Which two drove the most qualified traffic—people who actually signed up, bought, or engaged? Double down on those. Let the other four go quiet.

    You’ll lose the illusion of omnipresence. You’ll gain the reality of traction.

    Reply and tell us: which two channels are you concentrating on? We read every response.

  • Buffer vs. Publer vs. Hootsuite: scheduling APIs, queue limits, pricing

    Buffer vs. Publer vs. Hootsuite: scheduling APIs, queue limits, pricing

    Buffer vs. Publer vs. Hootsuite: scheduling APIs, queue limits, pricing
    Photo by Rubaitul Azad on Unsplash

    If you’re running content across Twitter, LinkedIn, Instagram, and Facebook, you need a scheduler that won’t drop posts, hit arbitrary limits, or charge you triple when you add a second brand account.

    Here’s how Buffer, Publer, and Hootsuite stack up for solo operators and small teams publishing 50–300 posts a month across 3–6 accounts.

    Queue depth and post limits

    Buffer’s free tier lets you schedule 10 posts per connected account. The Essentials plan ($6/month per channel) removes the queue cap but limits you to one user. If you’re managing client accounts or a team, you’ll need Team at $12/month per channel—which gets expensive fast if you’re active on four networks.

    Publer‘s free plan allows 10 scheduled posts total, but its Professional tier ($12/month) gives you unlimited scheduling across unlimited accounts and workspaces. That’s the same price Buffer charges per channel. If you’re cross-posting to five platforms, Publer saves you $48/month.

    Hootsuite’s pricing starts at $99/month for one user and 10 social accounts. The platform is built for agencies and enterprises—overkill unless you’re managing 20+ accounts or need approval workflows.

    Platform support and media handling

    All three handle Twitter, Facebook, LinkedIn, and Instagram. Publer also supports Pinterest, TikTok, YouTube, Mastodon, and Google Business—useful if you’re diversifying or testing new channels without adding another tool.

    Buffer compresses images above 5MB and doesn’t warn you before upload. If you’re scheduling high-res carousels or infographics, check the preview carefully—some detail gets lost. Publer and Hootsuite both preserve originals up to 8MB and 10MB respectively, then compress with a warning.

    Video support varies. Buffer caps video uploads at 512MB and transcodes everything to MP4. Publer allows up to 1GB and supports more native formats. Hootsuite allows 512MB but charges extra for bulk video libraries on lower tiers.

    Scheduling reliability and API quirks

    Buffer and Publer both use direct platform APIs. Posts queue client-side, then fire via webhook at the scheduled time. If the API is down (rare but it happens—Twitter’s API went dark for 90 minutes in May), your post waits in retry for up to four hours before failing silently. Neither tool emails you when a post fails unless you explicitly enable notifications in settings.

    Hootsuite uses a hybrid model: some posts go direct, others route through Hootsuite’s proxy layer for compliance monitoring. This adds 15–30 seconds of delay but catches posts that violate platform TOS before they go live. Helpful if you’re managing clients; irrelevant if you’re solo.

    One non-obvious issue: Instagram carousel scheduling fails on all three platforms if your images have mismatched aspect ratios. The API rejects the batch and gives a generic error. Publer’s preview catches this before you schedule; Buffer and Hootsuite don’t flag it until the post fails.

    Pricing summary

    • Buffer: $6/month per channel (Essentials), $12/month per channel for teams. Free tier: 10 posts per account, 3 channels max.
    • Publer: $12/month for unlimited posts and accounts (Professional). Free tier: 10 total scheduled posts, 3 accounts.
    • Hootsuite: $99/month (Professional), includes 10 accounts and one user. Free tier: 2 accounts, 5 scheduled posts.

    Who each is best for

    Pick Buffer if you’re only active on two or three platforms, post fewer than 50 times a month, and want the simplest possible interface. It’s clean, fast, and doesn’t try to upsell analytics dashboards you don’t need.

    Pick Publer if you’re cross-posting to four or more networks, testing new platforms like Mastodon or TikTok, or running multiple brands from one account. The per-channel pricing model makes it the cheapest option once you pass three active accounts.

    Pick Hootsuite only if you’re managing 10+ accounts for clients, need approval workflows, or require compliance logging for regulated industries. Everyone else is overpaying.

    One last note: all three platforms let you connect Google Analytics UTM parameters automatically. Buffer and Publer apply them at the account level; Hootsuite requires manual tagging per post unless you set up a campaign template. If attribution matters to you, test this before committing.

    Want more tool breakdowns like this? Subscribe to One Two Three Send for weekly comparisons, tutorials, and operator Q&A—no fluff, just the details that matter when you’re running a one-person content business.

    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.

  • WordPress plugin conflict logs: where errors hide and how to read them

    WordPress plugin conflict logs: where errors hide and how to read them

    WordPress plugin conflict logs: where errors hide and how to read them
    Photo by Brett Jordan on Unsplash

    When a WordPress plugin breaks your site, you usually get a white screen, a cryptic error message, or—worse—nothing at all. The checkout button stops working. The contact form silently drops submissions. A scheduled post never publishes.

    Most solo operators disable plugins one by one until something works again. That approach finds the culprit, but it doesn’t tell you why the conflict happened or whether it’ll break again after the next update.

    WordPress writes conflict and error data to several places. If you know where to look and what patterns matter, you can diagnose plugin conflicts in minutes instead of hours, and you’ll catch silent failures before readers do.

    Where WordPress writes plugin error logs

    WordPress doesn’t enable debug logging by default. When a plugin conflict occurs, the error might surface in your browser, but more often it gets written to a server log that most operators never open.

    There are three places to check:

    • debug.log in /wp-content/ — only appears if WP_DEBUG_LOG is enabled in wp-config.php. This is the primary conflict log. It captures PHP errors, warnings, notices, and deprecated function calls. Most plugin conflicts show up here first.
    • Server error logs — your hosting provider writes PHP fatal errors and web server errors here. Location varies: cPanel puts them in /logs/, managed WordPress hosts expose them in dashboards. BigScoots surfaces them in the control panel under “Error Log.” Fatal errors that kill the site bypass debug.log and land here.
    • Browser console — JavaScript conflicts don’t write to PHP logs. Open your browser’s developer tools (F12) and check the Console tab. A missing .js file, a jQuery version mismatch, or a React component error will show here, not in WordPress logs.

    If WP_DEBUG_LOG isn’t enabled, add this to wp-config.php just above the line that says /* That's all, stop editing! */:

    define('WP_DEBUG', true);
    define('WP_DEBUG_LOG', true);
    define('WP_DEBUG_DISPLAY', false);

    This writes errors to debug.log without displaying them to site visitors. Leave it on for a week. If a conflict exists, it’ll log.

    What plugin conflict entries actually look like

    A typical conflict log entry looks like this:

    [23-Jul-2026 14:32:18 UTC] PHP Fatal error: Uncaught Error: Call to undefined function wp_mail() in /home/user/public_html/wp-content/plugins/custom-contact-form/contact.php:47

    Here’s what matters:

    • Timestamp — when the error fired. Cross-reference this with when you updated a plugin or when a visitor reported a problem.
    • Error type — Fatal errors break execution. Warnings and notices don’t stop the site, but they signal a conflict that might escalate after the next plugin update.
    • File path — tells you which plugin triggered the error. In this case, custom-contact-form is the culprit.
    • Function namewp_mail() is undefined, which usually means the plugin loaded too early, before WordPress core functions were available.

    Not all conflicts are this clear. Sometimes you’ll see dozens of deprecation notices from a single plugin. That’s noise. Look for fatal errors and repeated warnings tied to the same plugin and function. Those are the patterns that predict breakage.

    Tracing breaking changes after plugin updates

    If your site breaks immediately after a plugin update, the log will show the new error. But if the conflict is silent—forms stop submitting, emails stop sending, scheduled posts don’t publish—you need to correlate log timestamps with plugin update times.

    WordPress logs plugin updates in the database, but doesn’t surface them in a readable timeline. If you suspect a recent update caused a conflict, check debug.log for the first error timestamp, then compare it to your plugin update history in the WordPress admin under Dashboard → Updates. That page shows recent updates, but only for the last few days.

    For longer history, query the database directly or use a plugin like WP Activity Log, which tracks every plugin activation, update, and deactivation with timestamps. Cross-reference the log with your debug.log entries. If the first error appeared within an hour of a plugin update, that’s your conflict source.

    What to do once you’ve identified the conflict

    Once you know which plugin is logging errors, you have three options:

    • Roll back the plugin — most managed WordPress hosts offer one-click plugin rollback. If yours doesn’t, download the previous version from the WordPress plugin repository’s “Advanced View” tab and upload it manually. This buys time while you wait for a patch.
    • Disable the conflicting feature — some plugins let you toggle features. If the conflict stems from a feature you don’t use, turn it off instead of disabling the entire plugin.
    • Contact the developer — if the plugin is actively maintained, report the conflict with your log excerpt, WordPress version, PHP version, and conflicting plugin name. Most developers fix conflicts within a few days if you give them clean reproduction steps.

    Don’t leave WP_DEBUG enabled indefinitely on a live site. Once you’ve identified and resolved the conflict, set WP_DEBUG and WP_DEBUG_LOG back to false. Debug logs grow large and can expose server paths to anyone who guesses the debug.log URL.

    One non-obvious tip: check for JavaScript conflicts separately

    PHP logs won’t capture JavaScript conflicts. If a plugin breaks your site’s front-end interactivity—buttons stop responding, modals don’t open, checkout forms freeze—open your browser console and look for red error messages. Common culprits: two plugins loading different versions of jQuery, or a plugin enqueueing a script that depends on another script that didn’t load.

    To confirm a JavaScript conflict, disable plugins one by one while keeping the browser console open. When the red errors disappear, you’ve found the plugin responsible.

    Want more WordPress infrastructure breakdowns like this? Reply with the hosting or plugin behaviour you want explained next—we read every response and route the best questions into future issues.