Every time I published a blog post, I’d have to rewrite it into a shorter version, throw in some hashtags, and post it to X separately. It’s a small task, but a surprisingly tedious one, and I’m someone who wants to automate everything I possibly can.
“Couldn’t an AI just post this for me automatically?”
That thought sent me down a rabbit hole of every paid service and plugin I could find.
None of them quite fit, and the setup for most of them was more hassle than it was worth. On top of that, I kept running into warnings that fully automated posting could get an account flagged as a bot and banned.
So I brought the problem to Claude.
Here’s the short version: with n8n (a no-code automation tool), you can post to X automatically if you’re paying for X’s API access. On a free account, the fully-automatic posting I originally wanted just wasn’t possible.
So instead, I built a workaround: n8n drafts the post text and hashtags and emails them to me, I glance over the text, and one button click opens the X compose window with everything already filled in. It’s not zero-touch, but it’s about as close as I could get for free — two clicks and I’m done.
Ha! ( ´θ`)ノ
- 1 Every paid option turned out to be a dead end
- 2 The overall design
- 3 Snag #1: just connecting n8n and X was a whole ordeal
- 4 Snag #2: a security scare that made me sweat
- 5 Snag #3: notifications kept mysteriously breaking
- 6 Update: two months later, scheduled posts stopped triggering it
- 7 Final thoughts
Every paid option turned out to be a dead end
Direct X API access: $100/month is a lot to ask
Using X’s API directly seemed like the simplest route, so I looked up the pricing. Turns out the free tier doesn’t mention posting access anymore. The Basic plan runs $100/month. (Checked as of August 2026.)
I’m not exactly a high-volume poster — 10 to 20 posts a month on this blog at most — so that price tag felt steep. Still, on the off chance that just the posting part could be worked around somehow, I decided to go ahead and wire it up directly in n8n anyway.
IFTTT: the free tier can’t do it anymore
IFTTT used to be my go-to for free automation, so I opened it up expecting the usual. Both the WordPress trigger and the RSS trigger turned out to require Pro once you actually try to do anything useful with them. Fair enough, I guess.
Auto-posting itself was technically possible, but it needed ongoing babysitting, and since it fires the moment it detects a new RSS item, editing a post afterward risks triggering a repost — which could easily read as spam to X. So I passed on this one too.
Buffer: automation is a paid feature
I looked into Buffer too. Automating the RSS-to-X pipeline needs a paid plan (billed per channel), and the free tier’s limits get restrictive fast if you actually want to rely on it.
If you don’t mind opening the dashboard and manually queueing each post, it’s free with a daily limit — that part does work. But that’s not really automation, is it?
What I actually wanted was for it to just happen — no dashboard, no queueing, the moment a post goes live.
That’s the whole point (*´ー`*)
Stopgap: having Claude write it, then copy-pasting it myself
So in the meantime, I had Claude Code generate the X post text and pasted it in myself.
The flow was simple enough — ask for a free-tier-friendly post with hashtags for the new article, get the text back instantly, paste it in. Done.
It only takes about 30 seconds each time, which is fine in the grand scheme of things, but it’s still one more manual step I’d rather not think about.
In the end, building it myself in n8n — a no-code tool for wiring up automations — turned out to be the cheapest and most flexible route, rather than relying on any paid service.
On top of the cost, there was another reason nagging at me: X has apparently gotten stricter about monitoring automated API activity, and accounts can get suspended for it.
I never dug too deep into exactly how bans get triggered, but the thought of it happening out of nowhere was enough to make me think: maybe a human should be the one actually pressing “post,” not an API.
If you go fully automatic, posts go out with zero review, over and over, and eventually the account could get flagged as something no human is actually operating and get suspended for it. That thought was already quietly sitting in the back of my mind at this point.
The overall design
Simplified, here’s the flow I wanted:
- When Claude schedules a post, it also has the AI write the X post text and saves it into a custom field (a WordPress feature that lets you attach arbitrary extra data to a post) alongside the body. I get to review and edit that text beforehand too, so by the time the post goes live, all that’s left is pressing a button.
- WordPress notices the post went live and pings n8n to say so.
- n8n picks that up, grabs the saved X post text, and emails it to me — post button included.
In short, I wanted the X post text to land in my inbox the instant the article went live — whether it was a scheduled post or published immediately, and even with multiple posts queued up, each one keeping its own identity so nothing got crossed.
Sounds simple enough, right? But actually building it turned into a much longer swamp than I expected. And that’s honestly the fun part — let me walk you through how it finally came together (*´ー`*)
Snag #1: just connecting n8n and X was a whole ordeal
Just wiring n8n, X, and WordPress together took several days on its own. Let me break it down piece by piece.
X needs to be told “yes, this app is allowed to connect”
To use the X API, you first have to register your app on X’s developer portal. Part of that registration requires a Callback URL (the address X redirects back to once authentication finishes) that’s actually reachable from the outside internet.
My home NAS isn’t normally reachable from outside, so I used Tailscale’s Funnel feature (a way to safely expose something on your home network to the internet) to expose it temporarily. I figured I could just close it again once authentication was done. That assumption came back to bite me hard later — but at the time, I had no idea.
n8n didn’t know its own address
The Webhook URL n8n generates (the address external services call to say “hey, this happened”) somehow ended up being 0.0.0.0 — an address that points to nowhere in particular. Naturally, nothing could reach it from outside.
The cause was a missing set of environment variables. I had to set N8N_HOST, N8N_PROTOCOL, and WEBHOOK_URL to tell n8n what its own address actually was, then restart the Docker container running it.
Except Synology’s Container Manager (the GUI on top of Docker) doesn’t let you edit those from its interface, so I ended up deleting the container and rebuilding it from scratch.
Authenticating from a local address just doesn’t work
X’s OAuth (an authentication scheme that lets two services connect securely without sharing passwords) kept rejecting me with Unauthorized errors. I’d just set everything up, so I had no idea why it kept failing (;´Д`A
Turned out I was accessing n8n through a local-only address. Authentication assumes you’re being accessed from somewhere publicly visible, so a local IP that nobody outside can see gets rejected. Opening n8n through Tailscale Funnel’s public URL and authenticating from there was the fix. I honestly hadn’t considered that a local IP and a public URL would behave differently for this.
Authentication finally worked — but posting still didn’t
After finally getting through the authentication maze, I tried the part that actually creates the post (the Create Tweet node). What came back was a 402 — “payment required.”
At first I figured it was just a temporary credit issue. But it didn’t clear up no matter how long I waited, and the more I dug, the worse it looked.
The answer I eventually landed on: creating a post isn’t part of the free tier at all — it’s gated behind that same $100/month Basic plan I’d seen at the very start. That was the punchline.
Authenticating and reading are free. Writing — the one thing I actually needed — is the part locked behind the paywall. Of course it is (;´Д`A
Paying $100 a month just to post to X from a personal blog wasn’t something I could justify. So I changed course: I gave up on having n8n post to X directly and ripped out the Create Tweet node entirely.
In its place, I used something X provides for free — a “tweet intent” URL (https://twitter.com/intent/tweet?text=◯◯). Opening this URL pulls up X’s compose window with your text already filled in. No authentication, no API key required.
WordPress’s signal wasn’t getting through
n8n has no built-in way to know when a WordPress post goes live, so I added a snippet to WordPress itself that sends a direct message to n8n the instant a post publishes. I confirmed the message made it from ConoHa (the external server hosting this blog) through Tailscale Funnel successfully.
At this point, n8n, X, and WordPress were finally all connected. But two more walls were still waiting.
Snag #2: a security scare that made me sweat
Just as I was starting to relax about having everything connected, something much scarier turned up.
I found out n8n had a serious vulnerability — CVE-2025-68613, rated 9.9 out of 10 in severity, about as bad as it gets. Given I had it exposed to the internet through Funnel, this was very much my problem. I updated n8n to the latest version immediately.
I also added a secret token to the webhook (a simple key-like check that ignores any request without the right password), so random requests from strangers would get rejected outright.
Leaving something exposed to the internet just because it’s free and convenient is genuinely risky — this was the point where I felt the most on edge.
I also realized that Funnel shuts off whenever Synology restarts, so I registered a task in Task Scheduler to bring it back up automatically after every reboot. One small detail: right after a restart, the network isn’t fully stable yet, so I added a 30-second delay before it actually runs.
At this point, things were finally working — at least in a rough, functional sense.
Snag #3: notifications kept mysteriously breaking
Just when I thought it was finally running smoothly, a string of new problems showed up around notifications.
The Gmail notification suddenly stopped one day
Once the Create Tweet node was gone, this Gmail step became the actual mechanism for “posting to X.” And then, one day, it just stopped. The cause was an expired Gmail authentication token (OAuth2).
When I tried to re-authenticate, Funnel had been closed again, so I got hit with another Unauthorized error… In the end, I moved away from the OAuth-dependent Gmail setup entirely and switched to SMTP (a standard, password-based way of sending mail), which doesn’t expire and has been stable ever since.
Closing Funnel broke everything the next day
Thinking “authentication’s done, I can close this now,” I shut Funnel down — and by the next day, every webhook had stopped arriving. Funnel isn’t something you open just for authentication — it needs to stay on permanently. That Task Scheduler setup from earlier exists precisely because of this lesson.
The notification email was arriving completely empty
The email itself arrived fine, but the title, post text, and URL were all blank. The data WordPress was sending turned out to be nested one level deeper than I’d assumed — I was pointing to where I thought the data lived, but needed to dig one level further in. A small structural mismatch, but it took a surprisingly long time to spot.
With that fixed, I finally had it confirmed working end to end. The email with the X post button was arriving with the correct content. Phewwww ( ´ ▽ ` )
Update: two months later, scheduled posts stopped triggering it
Just when I thought it was stable, I noticed a strange pattern a couple of months later: scheduled posts weren’t triggering the X email, but manually publishing a post did.
Digging into it, the culprit turned out to be the type of “hook” I’d used in the WordPress snippet. WordPress offers several places to hook into “when something happens, do this,” and the one I’d used only fired on the exact moment someone manually clicks the publish button. Scheduled posts get published in the background by WP-cron (WordPress’s built-in scheduled-task mechanism), which never triggers that particular hook.
The fix was switching to a different hook — transition_post_status — that correctly catches the “scheduled → published” transition too.
add_action('transition_post_status', function($new_status, $old_status, $post) {
if ($new_status === 'publish' && $old_status !== 'publish' && $post->post_type === 'post') {
$x_post_text = get_post_meta($post->ID, 'x_post_text', true);
if (empty($x_post_text)) return;
$secret = '(secret)';
$body = json_encode([
'post_id' => $post->ID,
'title' => $post->post_title,
'url' => get_permalink($post->ID),
'x_post_text' => $x_post_text,
]);
wp_remote_post('(Tailscale URL)', [
'body' => $body,
'headers' => ['Content-Type' => 'application/json', 'X-Webhook-Secret' => $secret],
'timeout' => 10,
]);
}
}, 10, 3);If this hadn’t fixed it, my backup plan was to have n8n poll the WordPress API every morning and ask “was anything published today?” instead of waiting for a push notification. Less elegant, but it wouldn’t depend on Funnel’s timing at all, which might make it more reliable in the long run.
Final thoughts
Honestly, I didn’t expect this to turn into such a swamp. Looking up X’s API pricing went smoothly enough — it was everything after that (Tailscale, authentication, the layered notification data, WordPress’s different hook types) where one problem kept leading straight into the next.
In the end, I never got n8n posting to X fully automatically. But now, all I have to do is schedule a post — the email with the X post button shows up right away, and it’s just a click to check the text and hit post.
There’s still one small manual step left, but compared to copy-pasting everything by hand, it’s a massive improvement. If anyone out there is stuck wrestling with webhooks, hopefully at least this helps: authenticate from a public URL, not a local IP.







Leave a Reply