Artificial Intelligence

How to Automate Scheduled X Posts with Codex and xurl

A scheduled task researches and drafts each post, while a fixed-account skill handles secure publishing.

JS
Jay Simons
Author
How to Automate Scheduled X Posts with Codex and xurl

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:

  1. An X developer application with read-and-write user authentication.
  2. xurl, which stores the credentials and communicates with the X API.
  3. A fixed-account Codex skill that verifies the identity before every write.
  4. A Codex scheduled task that researches, checks history, drafts, and publishes.

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.

What You Will Need

Before starting, you will need:

  • Codex on a Mac with access to Scheduled tasks.
  • An X developer account and an application with read-and-write permissions.
  • Homebrew.
  • A dedicated or clearly identified X account for the automation.
  • A local project containing the source material or editorial context the agent should use.

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 xurl

Install the official X Developer Platform CLI with Homebrew:

BASH
brew install --cask xdevplatform/tap/xurl

Then verify that it is available:

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

Create an X Developer Application

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.

Know Which Credential You Need

X exposes several credentials that look interchangeable but are not:

  • An OAuth 2.0 Client ID and Client Secret are used for the user authorization flow in this tutorial.
  • A Consumer Key and Consumer Secret belong to OAuth 1.0a.
  • A Bearer Token is commonly used for app-only authentication and is not a substitute for the user context needed to publish as your account.

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.

Authorize the Account Locally

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:

ZSH
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:

BASH
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:

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

Put a Fixed-Account Skill in Front of the API

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:

TEXT
xurl-post/
├── SKILL.md
├── agents/
│   └── openai.yaml
└── scripts/
    └── post.py

You can invoke $skill-creator in Codex and describe what you want:

TEXT
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:

  1. Resolve the local xurl executable.
  2. Run xurl whoami --username my_handle before every post.
  3. Parse the returned JSON and require an exact username match.
  4. Refuse the write if authentication or identity verification fails.
  5. Pass the finished post as a direct process argument, never through shell interpolation.
  6. Publish with xurl post TEXT --username my_handle.
  7. Support repeated --media-id values for media that has already been uploaded.
  8. Provide a --dry-run mode that checks identity and request construction without posting.
  9. Never blindly retry a failed or interrupted publish.

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.

Test the Publishing Boundary Without Posting Garbage

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:

  • The expected account is authenticated and the request is constructed correctly.
  • A different account is authenticated and the script refuses to continue.
  • whoami fails or returns malformed data.
  • The post contains quotation marks, apostrophes, URLs, emoji, and line breaks.
  • One or more media IDs are passed as separate arguments.

Once those tests pass, use one real, editorially valid post as the end-to-end production test.

Give the Scheduled Task a Complete Editorial Workflow

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:

  1. Read the project’s current articles or other source material to refresh its understanding of the site’s subjects and voice.
  2. Retrieve the X account’s accessible post history.
  3. Search for recent stories from authoritative, preferably primary, sources.
  4. Compare the publication date with the date of the underlying study, release, or event.
  5. Reject exact and semantic duplicates.
  6. Draft one concise, source-linked post that accurately represents the evidence.
  7. Invoke $xurl-post exactly once.
  8. Report the source, final text, history check, and returned post ID or URL.
  9. Skip the run if research, history retrieval, or factual verification fails.

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:

TEXT
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:

TEXT
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, pause, 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.

Duplicate Prevention Should Fail Closed

Preventing identical text is easy. Preventing repeated stories is harder.

Before researching or publishing, retrieve up to 100 recent posts:

BASH
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:

  • Canonical source URLs with tracking parameters removed.
  • DOI or study title.
  • Named event, product release, or policy announcement.
  • The core factual claim.
  • Semantically equivalent prior posts with different wording or links.

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.

Test the Whole Workflow in Stages

Before leaving the task unattended, test it in increasingly consequential stages:

  1. Run the research and duplicate check without publishing.
  2. Verify that the chosen source is primary and the proposed claim matches it.
  3. Run the skill in dry-run mode.
  4. Authorize one real publish.
  5. Confirm the returned post ID at https://x.com/my_handle/status/POST_ID.
  6. Inspect the first several scheduled runs in the Codex Scheduled interface.

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

Troubleshooting

xurl Cannot Find the Account

Reauthorize using the explicit handle, then reset the defaults:

BASH
xurl auth oauth2 --app my-x-app my_handle
xurl auth default my-x-app my_handle
xurl whoami --username my_handle

Posting Returns 403

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.

Codex Cannot Reach api.x.com

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.

A Publish May Have Succeeded Before an Error

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.

The Scheduled Task Does Not Run

Check that:

  1. The task is active in Codex Scheduled.
  2. The selected project remains available.
  3. xurl is still available in the task environment.
  4. OAuth has not expired or been revoked.
  5. The task has the narrow network and local permissions it needs.

How to Shut It Down Safely

Pause 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:

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

Final Thoughts

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.

Sources

Comments (0)

Join the discussion and share your thoughts on this post.

💬

No comments yet

Be the first to share your thoughts!