# Mastodon API: Posting Reference

Mastodon is supported via the standard Mastodon API. OmniSocials posts to the specific instance and account that completed OAuth. Because Mastodon is decentralized, every account lives on a different server and is identified by its full `user@instance.tld` handle.

**Channel ID:** `mastodon`

## Supported content types

| Type | Supported |
|------|-----------|
| Feed post (toot) | ✅ |
| 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": "Posting from the API 🐘" },
    "accounts": ["your-mastodon-account-id"]
  }'
```

## Platform-specific options

| Field | Type | Description |
|-------|------|-------------|
| `mastodon.thread_parts` | array | Publish as a chained thread instead of a single toot. See [Posting threads](#posting-threads). |

Content warnings and per-post visibility are not yet exposed and may be added as optional keys in the future.

## Posting threads

To publish a thread, pass `mastodon.thread_parts`. Each part becomes its own toot, replying to the previous one natively.

```json
{
  "accounts": ["your-mastodon-account-id"],
  "content": { "default": "" },
  "mastodon": {
    "thread_parts": [
      { "text": "1/3 — a thread on the fediverse 🐘" },
      { "text": "2/3 — every instance sets its own rules" },
      { "text": "3/3 — and that is a feature, not a bug" }
    ]
  }
}
```

**Rules:**

- 2 to 25 parts. For a single toot, omit `thread_parts` and use `content.mastodon` (or `content.default`) instead.
- Each `text` is required, non-empty, and ≤ 500 characters (the reference mastodon.social limit; custom instances may differ).
- `media_urls` is optional per part (max 4 items per part).

## Media requirements

Mastodon media limits vary by instance. OmniSocials validates against the default mastodon.social limits:

| Media | Requirement (default) |
|-------|-----------------------|
| Image | JPEG, PNG, GIF, or WebP. Max 16 MB. Up to 4 per post. |
| Video | MP4, max 99 MB. Duration is instance-dependent — OmniSocials does not enforce a fixed cap. |

Some instances are more permissive. Some are stricter. A post that passes OmniSocials validation may still be rejected by a custom instance with tighter limits. In that case the error body includes the instance's rejection message.

### Alt text

The Mastodon community strongly values alt text on images — several instances even run bots reminding users to add descriptions. Pass it per media item by using an object entry instead of a bare string (works in `media_urls`, `media_ids`, and per-part thread media; max 1500 characters). It is sent as the media's `description`:

```json
{
  "content": { "default": "Morning ride before work 🐘" },
  "accounts": ["your-mastodon-account-id"],
  "media_urls": [
    { "url": "https://example.com/bike.jpg", "alt": "A red bicycle leaning against a brick wall" }
  ]
}
```

## Character limit

Default is 500 characters (per the reference mastodon.social config). Custom instances can raise or lower this. OmniSocials validates against 500 by default.

## Limitations

- Content warnings (CW) are not currently exposed through the API
- Per-post visibility (`public`, `unlisted`, `private`, `direct`) defaults to the account's default. A field to override this is planned but not shipped.
- Federation delays may mean the post is not immediately visible on other instances even after a successful publish response
