Skip to content
kazma.
Search Kazma Documentation & Site
ع

X publisher (official API)

Kazma tweets only through the official X API v2 using OAuth 1.0a user context. There is no scrape path, no Playwright/computer-use poster, and no app-only Bearer posting. X also supports OAuth 2.0 user tokens; this connector uses its four OAuth 1.0a credentials.

Ask Kazma to call repository_metrics before drafting a metrics post. The read-only tool measures its own source installation with the official generator and returns the commit, timestamp and fresh file, line, package, test-file, test-function and commit counts. It ignores cached METRICS.md figures, reports tracked changes, and refuses a measurement if the source changes while it runs. A wheel installation without the source checkout reports that measurements are unavailable.

The tool does not import tests or run pytest on the live server. It returns collected_tests, including parameterized cases, from a collection receipt produced by a successful trusted development/CI run. The receipt must match a SHA-256 fingerprint of the tracked source, tests, fixtures and collection configuration. Merge-only commits and checkout line endings do not invalidate it. The result reports the collecting environment and time.

Use the verified collected count for the test headline. Test functions are source definitions, not the headline test count; collected cases are not tests passed. A missing or stale receipt means omit the test count, not replace it with function definitions. To refresh it in the development checkout, stage new source/test files, run python scripts/generate_metrics.py --write --require-collected, and commit its outputs. A pre-commit/CI gate rejects a stale shipped receipt. Do not enable a general shell interpreter to refresh metrics. Posting still requires the normal X approval.

You doKazma does
X Developer account + Project + AppNothing until keys exist
App User authentication = Read and writeRefuses 403 with that hint
Generate the four OAuth 1.0a valuesStores them in the vault via Settings → X
Label the account AutomatedReminds you; cannot set the label via API
Approve outbound tweetsChat tools are always HITL. On the Web, your click on X Studio (/x) or /api/scheduled/x is the approval

Do not paste keys in chat. vault_store would put them in history. Settings → X writes connectors.x.* through ConfigStore, which vault-encrypts api_key / *_secret / access_token.

SurfacePathWhat it is
X Studio/xFirst-class composer + X-only planner. Post now, schedule, reschedule, thread hops, delete a live tweet, load a saved draft.
Scheduled/scheduledMixed clock: cron jobs and X posts. X Studio’s All clocks button opens this page on purpose.
Chat toolsTelegram / Discord / Slack / Web chatx_post / x_schedule_post / x_delete_post / x_cancel_scheduled_post — always HITL, even under YOLO.

Settings → X holds credentials, caps, independent X model bindings and reply subject policies. Compose, review and plan on /x.

  1. developer.x.com → create the application available to your account.
  2. User authentication settings → Read and write.
  3. Keys and tokens → API Key, API Key Secret, Access Token, Access Token Secret.
  4. Kazma Settings → X → paste the four values + your @handle → Save → Test (GET /2/users/me).
  5. Enable posting. Open X Studio (/x) to write and schedule.

Test proves the authenticated account’s identity. It does not prove write, mentions-read, media-upload or analytics access; confirm those capabilities in the developer console. Endpoint denials remain typed rejected outcomes.

Optional env (overrides ConfigStore when set): X_API_KEY, X_API_KEY_SECRET, X_ACCESS_TOKEN, X_ACCESS_TOKEN_SECRET. Kill-switches: KAZMA_X_POST=0 (all posting), KAZMA_X_SCHEDULE=0 (scheduling only).

The Web operator surface for posting. Alpine page xStudioPage() (static/js/x_studio.js); EN/AR strings in i18n/catalog/x_studio.py. Mutating calls send X-Requested-With and go through CSRF Origin checks.

Composer

  • Live ToU preview from evaluate_post (280 chars, @ / # / $ caps) — no network, no ledger write (POST /api/x/preview).
  • Post now → publish_x_post (POST /api/x/post). Your click is the approval.
  • Schedule → book_x_post (POST /api/scheduled/x). Caps and the 30-day duplicate hash are reserved at booking.
  • Generate drafts with X model → draft_posts (POST /api/x/generate): one to three alternatives from your brief, each fact-checked, saved to Drafts; nothing is sent to X.
  • Reference documents (Settings → X, connectors.x.drafting.reference_files): up to three text files, one path per line, relative to the active workspace or absolute. Generate reads them whole every time, so an edited file is used at the next draft. The drafter gets them as the facts its posts may state, keeping their limits and status words, and the fact check gets them as evidence a claim can cite. They hold at most 40,000 characters together. A named file that cannot be read stops the draft before any model call, and Settings warns when you save one. To draft posts about Kazma itself, name docs/docs/guide/what-is-kazma.md. The Composer shows which files a draft was written from (x_api/references.py).
  • Post length (Settings → X, connectors.x.drafting.post_length): short (one sentence), standard (up to two) or full (as many sentences as the post needs; the default), always within X’s limit. It is the length in the language contract the drafter and every check read, for posts written by Generate and by x_draft_posts; replies keep Reply length. Posts took the reply length until 2026-10-26, and the safety check rejected a three-sentence post for it.
  • Thinking models: every X model call (drafting and the checks) asks the model for its answer without hidden reasoning, where the provider has a switch for it (DeepSeek’s API does). Each call’s token limit is sized for the answer, and a thinking model’s reasoning counts against it: with deepseek-flash the evidence check spent its whole limit thinking and returned nothing. A check whose reply is still cut off says “cut off at its token limit”.
  • Reply-to accepts a tweet id or an x.com/…/status/… URL. After a successful Post now, the composer hops onto the new tweet id so the next send is a thread hop. Clear thread drops it.
  • Use on a saved save_proposal draft stamps proposal_id. On post or schedule the stored text wins over whatever is in the box; then that draft is marked used (posted or scheduled, with the tweet or booking id). Its siblings in the same set stay in Drafts — until 2026-09-25 posting one draft hid the whole set, and a partly posted set aged out on the 14-day clock meant for spent ones. Editing the box after Use clears the id on the client so a later type-in is not rewritten. An id that does not name exactly one saved draft (unknown, or a bare id of a multi-draft set) returns 400 — one post, one draft, the same rule the chat gate applies.

Planner (X-only)

  • Upcoming X posts from GET /api/x/queue, with a date-time field + Reschedule and Cancel (POST /api/x/queue/{operation_id}/reschedule and .../cancel). Each carries the revision it read: a booking that changed meanwhile answers 409, so a stale page never moves a post it did not show. The Scheduled page uses the same two routes.
  • Posted rows: open on X, Reply (loads the id into the composer), Delete (POST /api/x/delete — confirm first). Chat x_delete_post is still always-HITL.
  • Drafts from GET /api/x/drafts (artifact store list_proposals). The agent reads the same drafts with the list_proposals tool (read-tier, no approval), which also shows which drafts went out and how. save_proposal gives each X draft’s length as X counts it, and marks one that is too long.
  • Audit from GET /api/x/audit (x_audit.db).
  • All clocks → /scheduled (cron + X together). The Studio queue stays X-only so a reminder job does not sit next to a tweet.

Not in this version: media, polls, quote tweets, likes, follows, DMs, scrape, recurring identical tweets, native X schedule API.

These are Kazma caps on top of X’s own quota. They fail closed.

GuardDefaultWhy
Always-HITL (ALWAYS_HITL_TOOLS)x_post, x_delete_post, x_schedule_post, x_cancel_scheduled_postChat/agent tools: human sees the exact text. YOLO / grants / HITL-off cannot skip. Web Studio does not call these tools — the operator click is the approval.
Drafted, shown, then postedx_post, x_schedule_post, book_x_postA draft the agent saves (save_proposal) is never published in the same turn: the agent shows it and stops, and you ask to post it in a later message. Then the card says what it does — “Publish to X — public, can’t be undone”, the post shown whole, a Post now button (the other X cards say “Schedule post”, “Delete post” or “Cancel scheduled post”) — on the web and in Telegram, Discord and Slack. It offers no “Allow tool”, YOLO or “Approve for task”: those answers are refused and approve nothing. (2026-10-16: “check the metrics” ended in a post approved as part of the reading.)
Official API onlyPOST /2/tweetsAutomation must use the API, not the website.
No extra verbsno like / follow / DM / searchStays inside posting.
No write retrynetwork drop ≠ second POSTAvoids double-posts.
Length280 weighted charactersKazma’s conservative text limit; URLs and emoji use the server parser.
@mentionsmax 2 (excluding your handle)Unsolicited mention spam.
$cashtagsmax 1Ticker spam.
Hashtagsmax 4Hashtag stuffing.
Duplicate hash30 daysIdentical automated copies.
Volume8/day, 80/30dLocal rolling-window defaults, independent of X endpoint limits and billing. Raise only after checking your account’s permissions and budget.
User-AgentKazma/<ver> (self-hosted; official X API v2)X requires a UA.

Media, polls, quote posts and campaign analytics remain disabled pending individual capability verification. A post’s public counts are read only on request, by x_status with metrics=true. X’s create-post reference currently restricts quote posts to Enterprise; the pricing guide describes usage credits. Check the Developer Console for your own access and budget before enabling a richer-content increment. Any such increment must retain the same approval and durable publication contracts.

ToolHITLWhat
x_statusnoConfigured?, handle, remaining caps, the latest posts. recent (1–50, default 5) and offset page through every post Kazma made: posts_total is how many there are, and next_offset the next page’s offset until it is null; each post’s time is given in the cron time zone. With metrics=true it also reads each listed post’s likes, reposts, replies, quotes, bookmarks and impressions from X (one read from the X read budget); a post X did not return is listed in metrics_missing, and a failed read in metrics_error — a missing count is never shown as zero. Never returns secrets.
x_postalwaystext, optional reply_to_id for a thread hop. Chat path requires a resolvable proposal_id (stored draft wins).
x_delete_postalwaystweet_id from a previous post.
x_schedule_postalwaystext + when (5m, 1h, or an ISO time such as 2026-12-01T09:00 in the cron time zone) + optional reply_to_id. One post, one time: a recurring time is refused. Approve once at booking. The reply gives the stored time and state, and says ok only when the post is scheduled.
x_list_schedulednoList scheduled X posts and their status.
x_draft_postsnobrief + count (1–3). Writes X posts the way X Studio’s Generate does — the X model, the reference documents set in Settings → X, the four checks — and saves them to X Studio’s Drafts under the running turn, so they are never published in the turn that wrote them. Answers with each draft’s id, text, X length and check verdicts. The model is told to use it for any X post it is asked to write; save_proposal is for text the user gave word for word. Nothing is sent to X.
x_download_medianopost (an x.com / twitter.com link, or the post id). Downloads the video, GIF or photos attached to a public post through the official X API (one read from the X read budget) into the workspace’s downloads/x folder — a video at the highest quality under 50 MB, the most a chat can be sent — and answers with the paths for send_file. Only X’s media hosts are fetched.
x_cancel_scheduled_postalwaysCancel a scheduled post before it fires (releases reserved quota).

These tools are the chat path. X Studio posts through publish_x_post / book_x_post / delete_x_post directly. Swarm workers stay capped by the commitment swarm-scope (outbound CRITICAL denied by default). A proposal_id is required on chat x_post / x_schedule_post — the commitment resolver rewrites text to the stored draft.

X has no native scheduled-post API — the /2/broadcasts/scheduled endpoint schedules live video streams (it needs an RTMP source_id), and POST /2/tweets has no scheduling field. So Kazma owns the clock: a scheduled post is stored in kazma-data/x_scheduled.db and the scheduler fires POST /2/tweets directly at the appointed time (the same client-side pattern X’s own docs describe).

  • Approve once at booking. x_schedule_post is always-HITL; you approve the exact draft + time when you book it. The fire is deterministic (no LLM re-reading the post).
  • Caps + dedupe are reserved at booking. Pending scheduled posts count toward the daily/monthly caps and the 30-day duplicate rule, so the schedule cannot be used to exceed them.
  • Double-post guard. A failed fire is never auto-retried on an ambiguous error (we can’t know whether it reached X). A 429 is deferred by the Retry-After window (bounded). A provably unsent post (the request never left, not_sent) is tried again a minute later, three tries in all, inside the five-minute window; anything else is marked failed and you’re notified. One row that fails does not stop the rows due after it: it is held, with a notice.
  • Rebooking. Booking the same draft again after it was cancelled, failed or expired is a new booking at the new time. While it is still booked at another time the answer is a conflict, never “scheduled”. Finished bookings are kept 90 days, then pruned.
  • Kill-switch. KAZMA_X_SCHEDULE=0 disables scheduling (and KAZMA_X_POST=0 disables posting entirely).

Timing limitation: a scheduled post fires only while the Kazma server is running. After a missed time, it can run within the five-minute grace period; later work is held for review and rescheduling. There is no unlimited catch-up. The server owns the UTC clock and dispatch is best effort. Local daylight-saving gaps are refused; ambiguous local times need an explicit UTC offset.

Manage the X content calendar in X Studio (/x). Open All clocks (or /scheduled) when you also need cron jobs. Chat tools stay in sync with both stores.

The Threads tab stores 2–25 ordered text segments. Edits and reordering use the same server composer autosave and revision conflict checks. Save the thread for review; all segments reserve quota in one transaction. Insufficient quota or a duplicate rolls back the entire reservation.

Approve the whole saved order and exact account revision. Each segment uses the ordinary publication service and the confirmed predecessor’s post ID as its reply target, following the official reply API. A thread is several remote posts, so partial publication is possible. It stops on any failed or uncertain send. A new approval resumes only known unsent segments; confirmed posts are skipped. An uncertain segment blocks resending until exact receipt reconciliation proves its outcome. Similar text is not proof.

Refresh review after an expired approval or interrupted execution. This grants no send permission. Cancel remaining posts to release unsent commitments; already published and uncertain posts remain recorded. Threads are attended immediate publication; their remaining segments are never dispatched by the schedule loop. To revise immutable reviewed text, cancel its unsent remainder and create a new thread. The review history retains actors and revisions.

Operations health reports process loop heartbeats, last successful cycles, tenant operation/reply states, model usage, repair lag and undelivered notices. Three consecutive loop failures enqueue one incident notice; a recovery enqueues one recovery notice. Delivery uses existing ops routing and requires an acknowledgement. Missing routing leaves a visible retry backlog. Reported usage is historical measurement; it is not an attestation that a model is available now.

IncidentOperator action
Authentication or write denialCheck the account’s app permissions; regenerate revoked credentials in Settings and Test the account identity. Re-review work bound to old credentials.
Model/checker unavailableLeave draft mode in place, fix the exact selected endpoint and rerun the candidate. Unknown checks cannot authorize auto publishing.
Database refusal or repair backlogRestore database availability and replay result projections. Never retry the remote send merely because a projection failed.
Rate limitingReview the stored defer-until time and account quota. Typed 429 refusals may defer schedules; uncertain requests cannot retry.
Unknown sendCheck exact correlated operation receipts. A missing search result or timeout cannot prove that nothing was posted.
Stopped or stale loopsCheck the process and health timestamps; restore the service through its managed guard. Late schedules remain held.
Restore or rollbackPause writes first. Restore the registered stores together; account Test and explicit resume are required. Restored thread approvals are invalidated and old work remains held.

The engineering restore rehearsal uses a real isolated bundle and verifies that published receipts survive, interrupted sends become unknown and queued work is held. Managed legacy projections are excluded from an older pending-only scheduler. This does not measure production recovery time or qualify an account. Before production rollback, back up the installation and keep writes disabled until its account and operation inventory have been reviewed.

Media, alt text, quote-post approval, recurring campaign drafts and engagement analytics are a capability-gated roadmap. No enabled control claims these features are available. Verify the account’s official endpoints first; future uploads must become durable dependencies and analytics must not be presented as proof of factual accuracy. The text Studio does not require those features.

Every X API call is recorded in an append-only audit trail — kazma-data/x_audit.db (SQLite WAL): local date-and-time with timezone, action (post / reply / delete / verify_credentials), endpoint, HTTP status, tweet id, the full request payload and full response body (success, HTTP error, and network failure alike), and duration. The hook lives at XClient._request — the single choke point covering the native skill, /api/x/*, and scheduled tweets. Recording is best-effort and never blocks the call. Inspect with any SQLite browser or query_x_audit(action="post", limit=50); nothing is auto-pruned (purge_x_audit(older_than_days=…) exists if you ever want retention).

  • Your X Developer Console is the source of truth for account access, usage credits and spend; Kazma’s local caps do not establish an X allowance.
  • An Automated label is your action in X settings; Kazma cannot flip it.
  • If keys leak, regenerate them in the dashboard, then Settings → X → Disconnect and save the new four.

This page covers posting and scheduling from your own account. Replying when the account is mentioned — locked against/support cards, or a summon emoji/word for one-offs — is X auto-reply. Same credentials, ToU policy, and audit log. Conversations on X Studio is the summon log (Approve / Deny / Retry / Delete).