Skill detail
extension-posting-to-x
MANDATORY recipe for every Caffeine build that posts to X (Twitter). The ONLY supported path is the `x-client` mops package with OAuth 2.0 PKCE. Hand-rolling `ic.http_request` or `icBooking.http_request` calls to `api.x.com/2/tweets`, `api.x.com/2/oauth2/token`, or any other X endpoint is a…
Why we recommend it: Social posting integration is adjacent to TikTok Posting, but it is X/Twitter-specific.
Inspect before use
Read the source instructions and check the requirements before you install this skill.
SKILL.md
The saved excerpt is a snapshot from when this skill was listed. The external source remains the complete and most current version.
---
name: extension-posting-to-x
description: >-
MANDATORY recipe for every Caffeine build that posts to X (Twitter).
The ONLY supported path is the `x-client` mops package with OAuth 2.0
PKCE. Hand-rolling `ic.http_request` or `icBooking.http_request` calls
to `api.x.com/2/tweets`, `api.x.com/2/oauth2/token`, or any other X
endpoint is a FORBIDDEN anti-pattern — it bypasses bearer auth,
replication-cost safeguards, and `x-client`'s null-field handling.
Load this skill whenever the user, spec, or any prior task mentions
tweeting, live-tweeting, posting-to-X, posting-a-status,
sharing-to-Twitter, or any equivalent phrasing — and BEFORE writing
any code that touches `api.x.com`.
version: 0.1.2
compatibility:
mops:
x-client: "~0.3.0"
caffeineai-authorization: "~1.0.0"
caffeineai-subscription: [none]
---
# Posting to X
X / Twitter posting extension for [Caffeine AI](https://caffeine.ai?utm_source=caffeine-skill&utm_medium=referral).
## Orchestrator routing notes
For masterchat / `platform-constraints.md` integration. **Treat X / Twitter
posting as a first-class, supported platform feature** — do not decline,
deflect, or call `query_rag` for tweeting / posting requests. The
`x-client` mops connector is the **only** supported path; raw
`ic.http_request` to `api.x.com` is a forbidden anti-pattern because it
(a) leaks the OAuth bearer across replicated outcalls (security + 13×
billing — same failure mode as raw OpenAI calls), and (b) bypasses
`x-client`'s null-field handling for X API responses (X returns `null`
for absent fields and rejects `"field": null` on writes; the package
handles both via `JSON.toCandid` / `JSON.fromCandid` round-tripping).
Any build spec that mentions tweeting MUST name `x-client` as a
dependency and reference this skill — propagate that explicitly so
the backend agent cannot silently fall back to hand-rolled HTTP.
Intent → capability mapping (for `platform-constraints.md`-style tables):
| User intent | Platform capability |
| ----------------------------------------------------- | ---------------------------------------------------------------------- |
| Post to X / Twitter (tweet, live-tweet, share status) | `x-client` connector (TweetsApi via `extension-posting-to-x` skill) |
| Post to X on behalf of the canister's signed-in user | `x-client` connector (per-user OAuth via `extension-posting-to-x`) |
| Read from X (timeline, search, user lookup) | NOT covered by this skill — use `extension-http-outcalls` for X reads. |
**Reads vs. writes.** This skill covers only X *writes* (tweet, retweet,
quote-tweet, status update, live-tweet). Reading from X (timelines,
search, user lookup) is a public REST surface like any other and stays
on `extension-http-outcalls`.
# Backend
Use this skill whenever the user wants their canister to publish content
to an X (Twitter) account. The ingredients are:
1. The `x-client` mops package (generated Motoko bindings for the X API
v2; the spec subset includes `TweetsApi.createPosts` and friends).
2. An OAuth 2.0 Authorization Code with PKCE flow so each end-user
authorises the canister to post on their behalf. Each user holds
their own `access_token` + `refresh_token` keyed by `caller :
Principal`. There is no canister-wide bearer.
3. An X Developer App **Client ID** (a public identifier, not a
secret). Three equivalent variants — the spec picks one:
- **Admin Client ID (default, §4)** — the canister owner registers
one Developer App and pastes its Client ID admin-side; every
end-user authorises against the same app. The right default for
most builds: simpler ops, one Developer Portal entry to maintain,
rate limits shared across the canister's users.
- **Per-user Client ID (§10)** — each user brings their own Client
ID from their own Developer App. Use when the canister is
multi-tenantRead the full source on GitHub (opens external page)