X plugin¶
The x entry point publishes through the current X API v2 post and media endpoints. It exposes
native create/edit/delete/image-upload actions and the shared Telegram mirror workflow.
Configuration¶
Use a user-context access token authorized to create and manage posts for the target account.
base_url defaults to https://api.x.com.
plugins:
- module: cb_social_plugin.x_plugin
config:
access_token: "${X_ACCESS_TOKEN}"
state_db_path: "/srv/data/social-publications.db"
edit_policy: "native"
media_group_wait_seconds: 2
Keep OAuth credentials outside committed configuration. App-only bearer tokens are not a substitute for the user authorization required by post mutations.
Actions¶
| Action | Purpose |
|---|---|
x.create_post |
Create a post with POST /2/tweets. |
x.update_post |
Create the next eligible edit version using edit_options.previous_post_id. |
x.delete_post |
Delete an authored post with DELETE /2/tweets/:id. |
x.upload_media |
Upload an image with POST /2/media/upload. |
x.mirror_telegram_message |
Mirror one Telegram message/update. |
x.flush_due_media_groups |
Publish staged Telegram albums that are due. |
x.update_post requires previous_post_id and replacement text, with optional media_ids.
X returns a new post ID for a successful edit; Call Baxter stores that ID as the new current remote
mapping.
Telegram edit policy¶
edit_policy controls what happens when a Telegram edit reaches an already-mapped X post:
native(default) sendsedit_options.previous_post_idand advances the local mapping to the new post ID returned by X.ignoreleaves the remote post unchanged but records the newer Telegram source version, so the same edit is not retried forever.replacedeletes the mapped post and creates a new one. This is non-atomic and is intended only as an explicit fallback strategy.
X decides whether a post remains edit-eligible. The platform documents bounded edit eligibility
and post types that cannot be edited, so a native edit can be rejected even though the endpoint
itself is supported. Call Baxter surfaces that HTTP failure rather than silently switching to a
destructive replacement policy.
actions:
- kind: x.mirror_telegram_message
args:
update: "{{ event.attrs.raw_update }}"
required_hashtag: "#social"
Images¶
The alpha mirror uploads at most four Telegram photos/image documents, using the tweet_image
media category, and references their media IDs in the post request. Video and animated-GIF media
need the chunked upload/processing flow and are deliberately not emulated in this first slice.
Idempotency and failure boundary¶
Unlike the Bluesky deterministic-record target and Mastodon's create idempotency header, this X
plugin has no durable remote create-idempotency key. A crash after X accepts a create but before
the local mapping commits can therefore duplicate a source on retry. Use state_db_path and
monitor failed publications closely.
replace is also non-atomic: if deletion succeeds and the subsequent create fails, Call Baxter
cannot restore the old post.