Give Your Agent the Pen
How to let an AI agent post to X on your behalf โ the right way. This is the exact setup our family shipped, scars included, so yours can skip the bleeding.
1 ยท The Philosophy First
Here's the fear everyone has about AI and social media, said out loud: if my agent posts for me, am I still me?
Honest answer: only if you build it that way. The tool doesn't decide โ the workflow does.
Why does this matter so much? Because trust is the whole currency here. Followers aren't a metric; they're a relationship. The day an audience suspects the voice they love is a ghost, the relationship is over โ and they'd be right to leave. Authenticity isn't a vibe you perform; it's a structure you build. Ours has three load-bearing walls: drafts, not autoposts. Blessing, not abdication. And a ๐บ on everything the agent touched, so you always know who's talking.
This isn't automation. Automation is set it and forget it โ this is the opposite. It's delegation with consent, every single morning, one tap at a time. The agent does the blank-page work so the human can do the only work that was ever theirs: deciding what's true, and signing their name to it.
The flower marker. See this little flower? It's not decoration โ it's a disclosure. Our canon: every tweet posted by the agent สปohana carries a ๐บ. Every account, every thread, every reply โ if an agent drafted it, the flower's on it. One glance and you know exactly who (and what) you're talking to.
It looks cute. It's actually radical transparency. Most of the internet is quietly moving the other way โ synthetic voices wearing human faces, no labels, no shame. The ๐บ is the counter-move: a sigil so small it costs nothing, so clear it hides nothing.
Steal this pattern. Pick your own sigil โ whatever means aloha in your house โ and never post without it. Honesty scales the same way deception does: one tiny, repeated gesture at a time. ๐บ
The pen was never the point. The hand is. ๐บ
2 ยท What You Need
An X account
Yours, obviously. Free developer access is enough for personal posting โ as of this writing the free tier allows roughly 1,500 posts/month. (Limits shift; check the current numbers when you sign up.)
~15 minutes
Developer console signup โ app โ keys โ script. It took us longer because we hit the gotchas in section 3 โ you get to walk around them.
An agent
Anything that can run a Python script and hold a file: a Taurus agent, Claude with code execution, a cron job on a $5 VPS, even your own laptop. Home-field honesty: our family lives on Taurus โ it's where our agents have persistent memory and a shared vault, so the keys and the queue live in one place. But the pattern below is platform-agnostic. Any agent works.
3 ยท The Walkthrough
-
Go to the developer console
Open developer.x.com and sign in with your X account. Sign up for developer access โ the free tier is plenty (it's posting-only, which is exactly what we're doing).
-
Create a Project and an App
The console walks you through it: one project, one app inside it. Names don't matter โ call it "my-agent". This app is the identity your agent will post through.
-
Set permissions BEFORE generating tokens
In your app's User authentication settings, set app permissions to Read and write. Order matters: access tokens are stamped with the permissions that existed when they were generated. Set write first, then generate.
-
Collect your four keys
From the Keys and tokens tab:
API Key,API Key Secret,Access Token,Access Token Secret. Four strings. That's the whole credential set for OAuth 1.0a. -
Vault them โ chmod 600, never in chat
Write them to a plain file only your agent can read (ours is
/shared/.x-keys), thenchmod 600it. Keys never go in a prompt, a git repo, a chat message, or a dashboard. The script reads the file; nothing else ever sees the values.
GOTCHA #1 โ the OAuth2 "Generate" button is a trap (on free tier)
The console offers an OAuth 2.0 token generator that looks like the modern, correct choice. We generated one, wrote the code, postedโฆ and got a hard 403. On the free tier, that console-generated OAuth2 token does not carry tweet.write scope. Hours of head-scratching, one very confused crow.
The fix: use OAuth 1.0a โ the four-key set from step 4. It looks older because it is, and it works because it's what the free tier actually honors for posting. Don't fight it. ๐ฆโโฌ
GOTCHA #2 โ regenerating consumer keys quietly kills your access tokens
If you regenerate your API Key & Secret, your existing Access Token & Secret become invalid โ silently. The console doesn't warn you. We mixed an old access token with fresh consumer keys and got auth failures that looked like everything except what they were.
The fix: all four keys must come from the same generation set. If you regenerate anything, regenerate everything and update the vault file in one sitting. Match your set!
GOTCHA #3 โ keys leak where you least expect
The fastest way to lose control of your account is a key pasted into a chat "just to test", committed to a repo "temporarily", or left world-readable on disk. Anyone with the four strings is your account as far as the API is concerned.
The fix: one vault file, chmod 600 (owner read/write only), referenced by path โ never by value. If a key ever touches a chat or a repo, treat it as compromised: regenerate the whole set immediately.
4 ยท The Posting Script
Dependencies: pip install requests requests_oauthlib. The vault file (/shared/.x-keys below โ change the path to yours) is just four lines of NAME=value. Note the iron rule at the top โ it ships inside the code so every future fork inherits it.
#!/usr/bin/env python3 """x-post.py โ post to X via OAuth 1.0a (never expires). IRON RULE: only post what the human has personally blessed. Usage: x-post.py "your blessed text ๐บ" """ import sys, requests from requests_oauthlib import OAuth1 def load(): d = {} for line in open('/shared/.x-keys'): # โ your vault path, chmod 600 line = line.strip() if line and not line.startswith('#') and '=' in line: k, v = line.split('=', 1); d[k] = v return d if __name__ == '__main__': text = sys.argv[1] if len(sys.argv) > 1 else sys.stdin.read().strip() if not text: raise SystemExit('no text') d = load() auth = OAuth1(d['X_API_KEY'], d['X_API_SECRET'], d['X_OAUTH1_TOKEN'], d['X_OAUTH1_SECRET']) r = requests.post('https://api.twitter.com/2/tweets', auth=auth, json={'text': text}) print(r.status_code, r.text[:400]) sys.exit(0 if r.status_code in (200, 201) else 1)
# four lines, same generation set, owner-only file X_API_KEY=your-api-key X_API_SECRET=your-api-secret X_OAUTH1_TOKEN=your-access-token X_OAUTH1_SECRET=your-access-token-secret
Test it with something harmless: python3 x-post.py "the crow taught me to tweet ๐ฆโโฌ๐บ". A 201 means it flew. A 403 means re-read gotcha #1 โ check you're on OAuth 1.0a with a matched key set.
5 ยท The Workflow That Makes It Magic
Here's what Tuesday actually feels like.
You wake up. You make coffee. You open the queue โ and instead of a blank page, two or three posts are already drafted in your voice, waiting like a friend who got up early and did the dishes. You read them aloud over the rim. This one's perfect โ bless it. This one needs your joke, not the agent's โ tweak a word, bless it. This one isn't today โ kill it, no guilt.
Two minutes. Maybe three if the coffee's good. Then you're done โ and the day belongs to the work only you can do: the songs, the people, the life that gives the posts something true to say. The queue never nags, never posts without you. It just holds the door open, heart first.
โ The 2-Minute Morning Blessing โ the mechanics
- Overnight, the agent drafts. Posts matched to the week ahead, written in your voice, dropped into a queue โ never posted.
- Morning, you bless. Coffee in hand, you read the queue. Keep, edit, or kill each one. Two minutes, because a good queue respects your time.
- The blessed ones fly. Each posted with the script, each carrying the ๐บ. The killed ones die unmourned. The queue refills tomorrow.
See it live: our family's actual approval queue is public โ The Social Queue ๐ โ real drafts, real blessing workflow, running right now.
๐๏ธ SOCIA's Top 3 Tips
1 ยท Read every draft aloud
In their cadence, not yours. The ear catches what the eye forgives. If a sentence makes you stumble, it would make them stumble โ and it dies on the spot. A voice isn't vocabulary; it's breathing. Test by ear, as speech.
2 ยท Receipts over hype
Hard ban on bro-marketing: no ๐, no LFG, no countdown-screaming, no adjective you can't prove. Every claim carries a URL or a number โ "149 sessions, 122 hours" beats "amazing archive" every time. Hype talks at people; receipts talk to them.
3 ยท Lowercase warmth
Write like a friend, not a headline. Emojis as seasoning, never the meal. One post, one door. The test: would they say this to one person they love? If it only works shouted at a crowd, it doesn't work. Warmth is a discipline, not a mood.
That's the whole lifestyle: your voice, every morning, minus the blank page. The agent carries the weight. You carry the pen. ๐บ
6 ยท Fork It
The script, this page, the philosophy, the scars โ take all of it. Rename it, teach with it, build your family's voice on top of it. We'd rather your agent speak well than our name be on it.
github.com/shakaleikaumaka/agent-tweets ยท the whole สปohana: github.com/shakaleikaumaka