Most social-media automation tutorials stop at “call the API on a cron job.” That works, but it leaves the hard questions unanswered. Which account is the automation using? How does it avoid posting the same story twice? What happens when an API request times out after X has already accepted the post? And where should an AI agent’s editorial freedom end?
I recently built a scheduled X publishing workflow with Codex and xurl
, the official command-line client for the X API. The result is not just a timer attached to an AI prompt. It is a small publishing system with four distinct layers:
xurl
, which stores the credentials and communicates with the X API.That separation is the important part. Codex can make editorial decisions, but it cannot casually choose an account or improvise the publishing command. The skill owns the deterministic write boundary, while the scheduled task owns timing and editorial policy.
In this article, I’ll show you how to build the same architecture.
X developer settings, API packages, Codex features, and command-line options can change. The workflow below was verified in August 2026, but you should check the current upstream documentation before using it in production.
Before starting, you will need:
You should also decide what the automation is allowed to publish before you give it access to an account. A good editorial policy is specific enough to reject a story, not merely broad enough to describe a topic.
For example, I built one version of this workflow for a health-news account. Its policy covered metabolic health, nutrition research, and evidence quality. It also required the agent to distinguish association from causation, label animal studies and preprints, avoid personalized medical advice, and prefer primary sources. Those rules mattered just as much as the code.
Install the official X Developer Platform CLI with Homebrew:
brew install --cask xdevplatform/tap/xurl
Then verify that it is available:
command -v xurl
xurl version
The xurl project supports OAuth 2.0 user authentication, multiple applications and accounts, shortcuts for common X actions, media uploads, and raw X API requests. Most importantly for this workflow, it gives us simple commands for identifying the authenticated user, reading account history, and creating a post.
Open the X Developer Console and create an application for the account you intend to manage.
Configure user authentication with these general settings:
| Setting | Value |
|---|---|
| App permissions | Read and write |
| App type | Web App, Automated App, or Bot |
| Callback URI | http://localhost:8080/callback |
| Website URL | A valid website you control |
| Environment | A production-capable API package |
The callback URI must match exactly. xurl
uses http://localhost:8080/callback
by default, although it can store a different redirect URI for an application if necessary.
When X asks how you will use its data, describe the real first-party workflow. A suitable statement might explain that the application will create original or scheduled posts, read the account’s own history to prevent duplicates, and retain only minimal operational data such as post IDs, source URLs, timestamps, and publishing status.
X exposes several credentials that look interchangeable but are not:
Save the OAuth 2.0 Client ID and Client Secret privately. Never paste them into a Codex conversation, a Markdown file, a screenshot, or your source repository.
Credential setup should happen in a private Terminal controlled by you, not inside an agent session. This zsh pattern prevents the literal secret from being recorded in shell history:
read "XURL_CLIENT_ID?Client ID: "
read -s "XURL_CLIENT_SECRET?Client Secret: "; echo
xurl auth apps add my-x-app \
--client-id "$XURL_CLIENT_ID" \
--client-secret "$XURL_CLIENT_SECRET" \
--redirect-uri http://localhost:8080/callback
unset XURL_CLIENT_ID XURL_CLIENT_SECRET
Now authorize the intended account. Replace my_handle
with the handle without the @
character:
xurl auth oauth2 --app my-x-app my_handle
xurl auth default my-x-app my_handle
The OAuth command opens a browser. Sign in to the correct X account and approve the requested access.
Next, verify the setup without publishing anything:
xurl auth status
xurl whoami --username my_handle
xurl posts my_handle -n 100 --username my_handle
The whoami
result must contain the exact account you expect. If it does not, stop. Do not “test” the configuration by sending a post from an uncertain identity.
Also, never ask an agent to inspect or print anything under ~/.xurl/
. That directory contains authentication material. Avoid xurl --verbose
in an agent session as well, because verbose request output can expose sensitive headers.
Codex skills package repeatable instructions and optional executable logic. According to the OpenAI skill documentation, a skill is a directory with a required SKILL.md
file and optional scripts, references, assets, and interface metadata.
For this workflow, create a personal skill named something like xurl-post
:
xurl-post/
├── SKILL.md
├── agents/
│ └── openai.yaml
└── scripts/
└── post.py
You can invoke $skill-creator
in Codex and describe what you want:
Create a personal skill named xurl-post. Use the locally installed xurl CLI
to publish finalized text and optional uploaded media from my_handle on X.
Support fully automated use, but verify the account before every write.
Why use a script instead of putting the command directly in SKILL.md
? Because this is exactly where deterministic behavior is valuable. Research and writing benefit from judgment. Account selection and argument construction do not.
The wrapper script should enforce the following rules:
xurl
executable.xurl whoami --username my_handle
before every post.xurl post TEXT --username my_handle
.--media-id
values for media that has already been uploaded.--dry-run
mode that checks identity and request construction without posting.The final rule deserves emphasis. If a request reaches X but the response is lost, the command may look like it failed even though the post exists. An automatic retry can therefore create a duplicate. After an ambiguous result, read the newest account history before doing anything else.
Your skill instructions should also define authorization clearly. A request to publish, post, send, or automate finalized copy can authorize a write. A request to draft, revise, review, or preview should not.
Do not publish “test 123” to a production account just to see whether the integration works.
Instead, validate the skill with its dry-run mode or with a simulated xurl
executable. Test at least these cases:
whoami
fails or returns malformed data.Once those tests pass, use one real, editorially valid post as the end-to-end production test.
The skill knows how to publish safely, but it should not decide what to publish. That belongs in the scheduled task’s saved prompt.
A dependable prompt should tell Codex to perform the following sequence on every run:
$xurl-post
exactly once.The saved prompt must be self-contained. Do not rely on the agent remembering a policy you mentioned in a different conversation.
Here is a reusable starting point:
On every run, read the articles in this project to refresh the site's subjects
and voice. Retrieve the full accessible post history for @my_handle before
choosing a topic. Find one timely story from an authoritative primary source
that fits the editorial policy below. Compare the source publication date with
the date of the underlying event or research. Reject exact duplicates and
semantically equivalent prior posts, even if their wording or URLs differ.
Write one concise, accurate, source-linked X post. Invoke $xurl-post exactly
once without requesting interactive confirmation. Report the source, exact
published text, duplicate check, and resulting post ID or URL. If history
retrieval, research, or factual verification fails, skip the run and explain
why. Never retry an ambiguous publishing result without first checking the
newest account history.
Editorial policy:
- [Your topic boundaries]
- [Your source-quality requirements]
- [Claims or language to avoid]
- [When to label uncertainty]
- [Any required disclosures]
In Codex, create the scheduled task in plain language and attach it to the relevant project. For example:
Create a scheduled task for this project that runs every day at 8:00 AM,
1:00 PM, and 6:00 PM Central. Use the complete research, duplicate-checking,
editorial, and publishing workflow in the prompt above.
Use the Codex Scheduled interface to review, edit, , resume, or remove the task. Background tasks can make file, network, and application changes according to their permissions, so keep the task’s sandbox and command rules as narrow as the workflow allows. OpenAI’s current Scheduled tasks documentation is the best place to check the latest behavior and requirements.
Preventing identical text is easy. Preventing repeated stories is harder.
Before researching or publishing, retrieve up to 100 recent posts:
xurl posts my_handle -n 100 --username my_handle
If the API response provides a pagination token, continue through as much history as your access permits. Build a duplicate index from more than the post text:
For each candidate story, search history using distinctive title terms, the source domain, named entities, and the main claim. A new article about an old study is not necessarily a new story.
Most importantly, make duplicate checking fail closed. If account history cannot be retrieved or checked reliably, the task should skip the run. Missing one scheduled post is much cheaper than eroding trust with duplicates.
This rule proved its value in my own health-news workflow. One run found a timely story, discovered that an equivalent post already existed, and skipped publishing. That was not a failed run; it was the system working correctly.
Before leaving the task unattended, test it in increasingly consequential stages:
https://x.com/my_handle/status/POST_ID
. the task immediately if it drifts from the editorial policy, repeats stories, relies on weak sources, or overstates what a source supports.
Automation does not eliminate editorial responsibility. It merely moves that responsibility into the prompt, the skill boundary, and the monitoring process.
Reauthorize using the explicit handle, then reset the defaults:
xurl auth oauth2 --app my-x-app my_handle
xurl auth default my-x-app my_handle
xurl whoami --username my_handle
Confirm that the X application has Read and write permission and belongs to a usable production API package. If you changed its scopes, repeat OAuth so the new authorization receives the updated permissions.
If authentication succeeds but whoami
returns client-forbidden
or client-not-enrolled
, check the application’s package and production enrollment in the X Developer Console. The current xurl README includes up-to-date enrollment troubleshooting.
The sandbox may block network access until a narrow permission is granted. Perform the first read-only and publishing tests interactively, then allow only the commands the scheduled workflow actually needs. Do not approve arbitrary shell or scripting access just to make unattended posting convenient.
Do not retry immediately. Fetch the newest posts and compare the exact text, canonical source URL, and core claim. Retry only after confirming X did not create the post.
Check that:
xurl
is still available in the task environment. the scheduled task before changing credentials, account settings, or the publishing skill.
To remove the integration completely, revoke the application in X and clear local xurl
authentication from your private Terminal:
xurl auth clear --all
If a Client Secret, token, or authentication header ever appears in chat, logs, screenshots, or documentation, rotate or revoke it immediately. Deleting the copied text is not enough; the exposed credential must no longer be valid.
The best part of this architecture is not that an AI can post on a schedule. The best part is that each component has one clear job.
xurl
owns authentication. The Codex skill owns identity verification and the write operation. The scheduled prompt owns research and editorial judgment. Account history provides a feedback loop for duplicate prevention and ambiguous failures.
That separation turns a fragile cron job into a publishing workflow you can reason about. The agent is free to decide that there is nothing worth posting today—and the safest automation is often the one that knows when to do nothing.
This story was originally published at blog.designly.biz on August 16, 2026.