# X (Twitter) API: Posting Reference

X (formerly Twitter) is supported via the X API v2. OmniSocials posts to the account that completed the OAuth flow. Both standard and premium accounts are supported, and the character limit adjusts based on the account's subscription tier.

**Channel ID:** `x`

## Supported content types

| Type | Supported |
|------|-----------|
| Feed post | ✅ |
| Story | - |
| Reel | - |

## Minimal example

```bash
curl -X POST https://api.omnisocials.com/v1/posts/create \
  -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": { "default": "Hello, X 👋" },
    "accounts": ["your-x-account-id"]
  }'
```

## Platform-specific options

| Field | Type | Description |
|-------|------|-------------|
| `x.reply_settings` | string | Who can reply: `""` (everyone, default), `following`, or `mentionedUsers` |
| `x.paid_partnership` | boolean | Mark as a paid partnership disclosure (default `false`) |
| `x.made_with_ai` | boolean | Mark as AI-generated content (default `false`) |
| `x.thread_parts` | array | Publish as a chained thread instead of a single tweet. See [Posting threads](#posting-threads). |

```json
{
  "content": { "default": "Limited reply post" },
  "accounts": ["your-x-account-id"],
  "x": {
    "reply_settings": "following"
  }
}
```

## Posting threads

To publish as a thread, pass `x.thread_parts` instead of relying on `content.x`. Each part becomes its own tweet, chained via `in_reply_to_tweet_id`.

```json
{
  "accounts": ["your-x-account-id"],
  "content": { "default": "" },
  "x": {
    "thread_parts": [
      { "text": "1/3 — kicking off a thread on building scalable APIs" },
      { "text": "2/3 — keep the boundary small, document at the edges" },
      { "text": "3/3 — and ship sooner than you think" }
    ]
  }
}
```

**Rules:**

- 2 to 25 parts. For a single tweet, omit `thread_parts` and use `content.x` (or `content.default`) instead.
- Each `text` is required, non-empty, and ≤ 280 characters. Parts that exceed the limit are rejected with a `400`.
- `media_urls` is optional per part (max 4) and overrides any top-level `media_urls.x` for that specific tweet:

  ```json
  {
    "x": {
      "thread_parts": [
        {
          "text": "Photo dump from the trip 📷",
          "media_urls": ["https://example.com/1.jpg", "https://example.com/2.jpg"]
        },
        { "text": "More to come tomorrow." }
      ]
    }
  }
  ```

- When `content.x` is provided alongside `thread_parts`, `thread_parts` wins.
- The first tweet's URL becomes `x_posted_url` on the post. The full chain is recorded on `x_values.thread_tweets` for analytics.

To convert a thread back into a single tweet on update, send `x.thread_parts: null` to `PATCH /posts/:id`.

## Character limits

| Account tier | Character limit |
|--------------|-----------------|
| Standard | 280 |
| Premium (Blue) | 25,000 |
| Premium+ | 25,000 |

For single tweets, OmniSocials detects the connected account's subscription tier and enforces the correct limit at validation time. Thread parts always cap at 280 chars regardless of tier — that's an X API constraint, not an OmniSocials one.

## Media requirements

| Media | Requirement |
|-------|-------------|
| Image | JPEG, PNG, GIF, or WebP. Max 5 MB. Up to 4 per post. |
| Video | MP4, H.264 codec, up to 2 minutes 20 seconds, max 512 MB |

X does not support mixing image and video in the same post. Video posts allow one video. Image posts allow up to four images.

**Alt text.** Pass a per-image accessibility description by using an object entry instead of a bare string — `{ "url": "https://example.com/chart.png", "alt": "Bar chart of Q3 signups by week" }` in `media_urls` (or `{ "id": "...", "alt": "..." }` in `media_ids`, including per-part thread media). It is set via X's v2 media metadata on photos and GIFs only (X does not support alt text on video), clamped to X's 1000-character cap. An alt-text failure at X never fails the publish.

## Limitations

- Polls are not supported through the X API v2 for third-party apps
- Posting to X requires approval by X for the OAuth app. Newly created X developer accounts may be subject to rate limits lower than the platform default.
