# X (Twitter): coming soon

Source: https://staging.postgale.com/docs/x

X support in Postgale is coming soon: the planned posts, threads, polls, media, replies, metrics and DMs through the Postgale REST API and MCP server.

> **Coming soon.** X is not available yet: X accounts cannot be connected in production, so none of the endpoints or tools
> below work against X today. This page describes the planned integration and may change before launch.

When X launches, Postgale will publish to X with the X API v2: posts, threads, polls, quotes, replies, images, GIFs and videos, with replies, metrics,
mentions and direct messages. Everything is available over the [REST API](https://staging.postgale.com/docs/api) (paths relative to `https://staging.postgale.com/api/v1`)
and the [MCP server](https://staging.postgale.com/docs/mcp) (`create_post`, `list_comments`, `moderate_comment`,
`get_analytics`, `send_message`, `start_conversation`, `x_engagement`,
`x_search_posts`, `x_lookup`). Each connected X user will be one account with platform `x`.

## Connect an account

Ask for a connect link and send the account owner to it. They authorize Postgale on X (OAuth 2.0); tokens are stored encrypted and refreshed automatically.

GET /api/v1/connect/x:
```bash
curl https://staging.postgale.com/api/v1/connect/x?externalId=cust-1 \
  -H 'Authorization: Bearer sk_…'

# {"authUrl":"https://x.com/i/oauth2/authorize?…","expiresInSeconds":600}
```

## Publish

Text is limited to 280 weighted characters (URLs count 23, emoji and CJK characters 2), or 25,000 for accounts with an X Premium
subscription. The limit is checked when the post is created. Videos are processed by X after upload; while that runs the target stays
`publishing` and the worker finishes it.

POST /api/v1/posts:
```bash
curl -X POST https://staging.postgale.com/api/v1/posts \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"content":"We just shipped scheduled threads 🧵","platforms":[{"accountId":"acc_x","platformSpecificData":{"threadItems":[{"content":"We just shipped scheduled threads 🧵"},{"content":"Write the thread once, pick a time, done.","mediaUrl":"https://media.postgale.com/demo.png","altText":"Thread editor"}],"replySettings":"following"}}],"publishNow":true}'
```

POST /api/v1/posts:
```bash
curl -X POST https://staging.postgale.com/api/v1/posts \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"content":"Which feature next?","platforms":[{"accountId":"acc_x","platformSpecificData":{"poll":{"options":["Bluesky","LinkedIn","YouTube"],"duration_minutes":1440}}}],"publishNow":true}'
```

| platformSpecificData | What it does |
| --- | --- |
| `customContent` | Text used for X instead of the post content (e.g. a shorter version) |
| `mediaUrl / mediaUrls` | Public https URLs: up to 4 images (5 MB each), or 1 GIF (15 MB), or 1 video (512 MB) |
| `mediaType` | text \| image \| gif \| video (default: from the URL extension) |
| `altText / altTexts` | Image alt text (≤1000 characters), per image |
| `threadItems` | A thread (≤25 posts) published as a reply chain: `[{content, mediaUrl?, mediaUrls?, altText?}]`; replaces the post content |
| `replyToTweetId` | Post id or URL to reply to (see the reply rule below) |
| `quoteTweetId` | Post id or URL to quote (not with media or a poll) |
| `replySettings` | following \| mentionedUsers \| subscribers \| verified (default: everyone) |
| `poll` | `{options: 2-4 strings ≤25 characters, duration_minutes: 5-10080 (default 1440)}`; not with media, threads or quotes |
| `madeWithAi / paidPartnership` | Disclosure labels |
| `sensitiveMedia` | `{adultContent?, graphicViolence?, other?}`: content warning on the media |
| `geoRestriction` | `{countries: [ISO codes, ≤25]}`: only show the media in these countries |
| `longVideo` | Upload the video as amplify_video (long videos, eligible accounts) |

## Endpoints

| Method | Path | What it does |
| --- | --- | --- |
| POST | `/posts` | Publish or schedule (targets with an X accountId and platformSpecificData above) |
| GET | `/inbox/comments?accountId=&platformPostId=` | Replies to a post (last 7 days) |
| POST | `/inbox/comments/{id}/reply` | Reply to a reply |
| POST | `/inbox/comments/{id}/hide` | Hide or unhide a reply to your post |
| POST | `/posts/{id}/edit` | Edit (X Premium, within 1 hour; creates a new post id) |
| POST | `/posts/{id}/unpublish` | Delete from X |
| GET | `/analytics/posts/{id}` | Impressions, likes, replies, reposts, quotes, bookmarks, clicks |
| GET | `/accounts/{id}/mentions` | Posts mentioning the account |
| POST / DELETE | `/x/accounts/{id}/reposts \| likes \| bookmarks` | `{postId}`: repost, like or bookmark, and undo |
| POST / DELETE | `/x/accounts/{id}/following` | `{userId \| username}`: follow and unfollow |
| GET | `/x/accounts/{id}/search?query=` | Recent search (last 7 days) |
| GET | `/x/accounts/{id}/posts/{postId}` | One post |
| GET | `/x/accounts/{id}/users/{username}` | One user (incl. receivesYourDm) |
| POST | `/inbox/conversations` | Start a DM conversation: `{accountId, participantUsername \| participantId, text \| mediaUrl}` |
| POST | `/inbox/conversations/{id}/messages` | Send a DM in a conversation |
| POST | `/inbox/conversations/sync` | Backfill DM conversations (`{accountId}`) |

## Direct messages

X has no 24-hour messaging window: you can message anyone whose DM settings accept messages from the account, and only X lets you
write first. Start with `POST /inbox/conversations`, then reply with `POST /inbox/conversations/{id}/messages`.
Encrypted X Chat conversations are not available through the X API.

POST /api/v1/inbox/conversations:
```bash
curl -X POST https://staging.postgale.com/api/v1/inbox/conversations \
  -H 'Authorization: Bearer sk_…' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"acc_x","participantUsername":"happy_customer","text":"Thanks for the shoutout! Here is 20% off: THANKS20"}'
```

## Comments, mentions and inbound events

Replies to your posts are listed from X's recent search (the last 7 days). When the instance enables the inbox poll
(`X_INBOX_POLL_MINUTES`), new replies arrive as `comment.received`, other mentions as
`mention.received`, and DMs as `message.received` webhooks.

## X's rules to know

- **Replies**: X only accepts API replies to a post whose author mentioned or quoted the account (or in the account's own conversations). Other replies fail with 403.
- **Likes, follows and quote posts** are restricted by X to Enterprise API access; they may fail with `missing_permission`.
- **Edits** need X Premium and must happen within 1 hour (up to 5 times); X gives the edited post a new id.
- **Costs**: X bills every API call (posts, reads, DMs) to the app. Posts containing a link cost noticeably more on X's side.
- Rate limits (per account, 15 minutes): 100 posts, 50 deletes, 15 DM sends; 429 answers `rate_limited`.
