# Typefully Public API

- **OpenAPI Version:** `3.1.0`
- **API Version:** `2.0.0`

The Typefully Public API allows you to programmatically manage your social media drafts, schedule posts, and publish content across multiple platforms.

## Important: X Automation Compliance

**Before building with this API for X automation, please review these critical guidelines:**

- Make sure to adhere to [X automation rules](https://help.x.com/en/rules-and-policies/x-automation) and [general X rules](https://help.x.com/en/rules-and-policies/x-rules) when scheduling content, otherwise your X account might be banned.
- If you plan to build an app on X that is not just for you or your company to use, you will need to use the [X API with higher rate limits](https://developer.x.com/en) than Typefully's API, which is meant to create personal automations and workflows.

## Authentication

All requests require a Bearer token in the Authorization header:

```properties
Authorization: Bearer YOUR_API_KEY
```

Generate your API key from your Typefully settings.

## Permissions & Access Levels

API keys inherit the same permissions as the user who created them. Your access to social sets (accounts) determines which API operations you can perform.

## Rate Limiting

API requests are rate-limited on a per user and per social set basis. When you exceed the rate limit, you'll receive a 429 Too Many Requests response. All API responses include headers showing your current rate limit status:

**User rate limits** (applies to all endpoints, per user): `X-RateLimit-User-Limit` (maximum requests allowed), `X-RateLimit-User-Remaining` (requests remaining), `X-RateLimit-User-Reset` (Unix timestamp when limit resets).

**Social set rate limits** (applies to specific operations like draft creation, per social set): `X-RateLimit-SocialSet-Limit`, `X-RateLimit-SocialSet-Remaining`, `X-RateLimit-SocialSet-Reset`, `X-RateLimit-SocialSet-Resource` (the resource identifier, e.g., "drafts.create").

## Pagination

List endpoints use limit-offset pagination for efficient data retrieval:

- **limit**: Maximum items per page (default: 10, max: 50)
- **offset**: Number of items to skip (default: 0)

Example request:

```bash
GET /v2/social-sets?limit=25&offset=50
```

Each paginated response includes:

- **results**: Array of items for the current page
- **count**: Total number of items available
- **limit**: Items per page used for this request
- **offset**: Current offset value
- **next**: URL for the next page (null if on last page)
- **previous**: URL for the previous page (null if on first page)

## Servers

- **URL:** `https://api.typefully.com`
  - **Description:** Production server

## Operations

### Get current user

- **Method:** `GET`
- **Path:** `/v2/me`
- **Tags:** Users

Retrieve the currently authenticated Typefully user associated with your API Key

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`email` (required)**

  `string` — Email address of the user

- **`id` (required)**

  `integer` — Unique identifier for the user

- **`name` (required)**

  `string` — Name of the user

- **`signup_date` (required)**

  `string`, format: `date-time` — Timestamp when the user signed up (ISO 8601 format in UTC)

- **`api_key_label`**

  `object` — Label of the API key used to authenticate this request. Null if no label was set.

- **`profile_image_url`**

  `object` — URL to the user's profile image. Null if not available.

**Example:**

```json
{
  "id": 12345,
  "name": "John Doe",
  "email": "user@example.com",
  "profile_image_url": "https://example.com/avatar.jpg",
  "signup_date": "2024-01-15T10:30:00Z",
  "api_key_label": "My Production Key"
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### List analytics posts

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/analytics/{platform}/posts`
- **Tags:** Analytics

Retrieve posts with metrics for a platform in a social set between `start_date` and `end_date` (inclusive).

**Required permission:** READ access to this social set.

Notes:

- `platform` is a path parameter (for example: `x`, `linkedin`).
- This endpoint is social-set scoped and platform-scoped to support analytics expansion.
- Currently, only `x` is supported.
- Replies are excluded by default. Set `include_replies=true` to include them.
- Pagination defaults to `limit=25` with max `limit=100`.
- Date ranges larger than 366 days are rejected.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `platform` required

- **In:** `path`

`string`

##### `start_date` required

- **In:** `query`

`string`

##### `end_date` required

- **In:** `query`

`string`

##### `include_replies`

- **In:** `query`

Include X replies in the results. Defaults to `false`, which returns only non-reply posts.

`boolean`, default: `false` — Include X replies in the results. Defaults to \`false\`, which returns only non-reply posts.

##### `limit`

- **In:** `query`

Maximum number of items to return per page

**Any of:**

`integer`

`null`

##### `offset`

- **In:** `query`

Number of items to skip from the beginning

`integer`, default: `0` — Number of items to skip from the beginning

#### Responses

##### Status: 200 Paginated posts with normalized analytics metrics.

###### Content-Type: application/json

- **`limit` (required)**

  `integer`

- **`next` (required)**

  `object`

- **`offset` (required)**

  `integer`

- **`previous` (required)**

  `object`

- **`results` (required)**

  `array`

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — When the post was created (ISO8601 in UTC).

  - **`metrics` (required)**

    `object` — Normalized post metrics grouped for external API consumers.

    - **`engagement` (required)**

      `object` — Normalized engagement metrics for this post.

      - **`comments` (required)**

        `integer` — Comment/reply count for the post.

      - **`likes` (required)**

        `integer` — Reaction count for the post.

      - **`profile_clicks` (required)**

        `integer` — Profile click count.

      - **`quotes` (required)**

        `integer` — Quote/repost-with-comment count.

      - **`shares` (required)**

        `integer` — Reshare/repost count for the post.

      - **`total` (required)**

        `integer` — Total engagement count across supported interaction metrics.

      - **`link_clicks`**

        `object` — Outbound URL click count when available.

      - **`saves`**

        `object` — Save/bookmark count when available.

    - **`impressions` (required)**

      `integer` — Number of impressions for the post.

  - **`platform` (required)**

    `string`, possible values: `"x"` — Social platform for this post. Currently only \`x\` is supported.

  - **`post_id` (required)**

    `string` — Platform post identifier serialized as a string.

  - **`preview_text` (required)**

    `string` — Post preview text content.

  - **`url` (required)**

    `string` — Public URL to the post.

  - **`draft_id`**

    `object` — Typefully draft ID that produced this post when available.

**Example:**

```json
{
  "results": [
    {
      "platform": "x",
      "post_id": "1898022122726803672",
      "draft_id": 1,
      "created_at": "",
      "preview_text": "",
      "url": "",
      "metrics": null
    }
  ],
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Get followers analytics

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/analytics/{platform}/followers`
- **Tags:** Analytics

Retrieve daily follower counts for a platform in a social set between `start_date` and `end_date` (inclusive).

**Required permission:** READ access to this social set.

Notes:

- `platform` is a path parameter (for example: `x`, `linkedin`).
- This endpoint is social-set scoped and platform-scoped to support analytics expansion.
- Currently, only `x` is supported.
- Returns a totals-only daily follower series with the latest in-range follower count.
- If `start_date` and `end_date` are omitted, defaults to the last 30 days.
- Date ranges larger than 366 days are rejected.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `platform` required

- **In:** `path`

`string`

##### `start_date`

- **In:** `query`

Start date for the follower series (YYYY-MM-DD). Defaults to 29 days before `end_date`, or before today when `end_date` is omitted.

**Any of:**

`string`

`null`

##### `end_date`

- **In:** `query`

End date for the follower series (YYYY-MM-DD). Defaults to today in the social set's timezone.

**Any of:**

`string`

`null`

#### Responses

##### Status: 200 Daily follower counts for a social set on X.

###### Content-Type: application/json

- **`data` (required)**

  `array` — Daily follower counts ordered ascending by date.

  **Items:**

  - **`date` (required)**

    `string`, format: `date` — Date bucket in the social set's timezone (YYYY-MM-DD).

  - **`followers_count` (required)**

    `integer` — Follower count for the date bucket.

- **`platform` (required)**

  `string`, possible values: `"x"` — Social platform for this analytics series. Currently only \`x\` is supported.

- **`current_followers_count`**

  `object` — Latest non-null follower count in the returned date range.

**Example:**

```json
{
  "platform": "x",
  "current_followers_count": 1,
  "data": [
    {
      "date": "",
      "followers_count": 1
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 503 Service unavailable

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### List social sets

- **Method:** `GET`
- **Path:** `/v2/social-sets`
- **Tags:** Social Sets

Retrieve all social sets (accounts) you can access. This includes accounts you own directly and accounts that belong to teams you are a member of.

#### Parameters

##### `limit`

- **In:** `query`

Maximum number of items to return per page

**Any of:**

`integer`

`null`

##### `offset`

- **In:** `query`

Number of items to skip from the beginning

`integer`, default: `0` — Number of items to skip from the beginning

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`count` (required)**

  `integer`

- **`limit` (required)**

  `integer`

- **`next` (required)**

  `object`

- **`offset` (required)**

  `integer`

- **`previous` (required)**

  `object`

- **`results` (required)**

  `array`

  **Items:**

  - **`id` (required)**

    `integer` — Unique identifier for the social set

  - **`name` (required)**

    `string` — Display name of the social media account

  - **`profile_image_url` (required)**

    `object` — URL to the profile image of the social media account. Null if not available.

  - **`username` (required)**

    `string` — Username/handle for the social media account

  - **`team`**

    `object` — Team that owns this social set. Null if the social set is owned by an individual user.

**Example:**

```json
{
  "results": [
    {
      "id": 12345,
      "username": "elonmusk",
      "name": "Elon Musk",
      "profile_image_url": "https://typefully-user-avatars.s3.amazonaws.com/_generic/account/537/twitter.jpeg",
      "team": null
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Get social set details

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/`
- **Tags:** Social Sets

Retrieve detailed information about a social set, including every configured social media platform (X, LinkedIn, Mastodon, Threads, Bluesky) with account details and profile information.

**Required permission:** READ access to the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`id` (required)**

  `integer` — Unique identifier for the social set

- **`name` (required)**

  `string` — Display name of the social media account

- **`platforms` (required)**

  `object` — All platform accounts configured in this social set (X, LinkedIn, Mastodon, Threads, Bluesky)

  - **`bluesky` (required)**

    `object`

  - **`linkedin` (required)**

    `object`

  - **`mastodon` (required)**

    `object`

  - **`substack` (required)**

    `object`

  - **`threads` (required)**

    `object`

  - **`x` (required)**

    `object`

- **`profile_image_url` (required)**

  `object` — URL to the profile image of the social media account. Null if not available.

- **`username` (required)**

  `string` — Username/handle for the social media account

- **`publishing_quota`**

  `object` — Shared publishing quota snapshot for this social set.

- **`team`**

  `object` — Team that owns this social set. Null if the social set is owned by an individual user.

**Example:**

```json
{
  "id": 12345,
  "username": "elonmusk",
  "name": "Elon Musk",
  "profile_image_url": "https://typefully-user-avatars.s3.amazonaws.com/_generic/account/537/twitter.jpeg",
  "team": {
    "id": "abc123def4567890",
    "name": "Marketing Team"
  },
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null
  },
  "publishing_quota": {
    "used": 1,
    "remaining": 1,
    "resets_at": ""
  }
}
```

##### Status: 403 You do not have permission to access this social set

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### List drafts

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/drafts`
- **Tags:** Drafts

Retrieve all drafts for a specific social set with optional filtering and sorting. Drafts are ordered by last edited date (most recent first) by default.

**Draft statuses:** 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed.

**Required permission:** READ access to this social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `status`

- **In:** `query`

Filter by draft status. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed.

**Any of:**

`string`, possible values: `"draft", "published", "scheduled", "planned", "error", "publishing"`

`null`

##### `tag`

- **In:** `query`

**Any of:**

**Array of:**

`string`

`null`

##### `order_by`

- **In:** `query`

**All of:**

`string`, possible values: `"created_at", "-created_at", "updated_at", "-updated_at", "scheduled_date", "-scheduled_date", "published_at", "-published_at"` — Allowed order\_by fields for draft listing - prevents SQL injection

##### `limit`

- **In:** `query`

Maximum number of items to return per page

**Any of:**

`integer`

`null`

##### `offset`

- **In:** `query`

Number of items to skip from the beginning

`integer`, default: `0` — Number of items to skip from the beginning

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`count` (required)**

  `integer`

- **`limit` (required)**

  `integer`

- **`next` (required)**

  `object`

- **`offset` (required)**

  `integer`

- **`previous` (required)**

  `object`

- **`results` (required)**

  `array`

  **Items:**

  - **`bluesky_post_enabled` (required)**

    `boolean` — Whether posting to Bluesky is enabled

  - **`bluesky_post_published_at` (required)**

    `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

  - **`id` (required)**

    `integer` — Unique identifier for the draft

  - **`linkedin_post_enabled` (required)**

    `boolean` — Whether posting to LinkedIn is enabled

  - **`linkedin_post_published_at` (required)**

    `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

  - **`mastodon_post_enabled` (required)**

    `boolean` — Whether posting to Mastodon is enabled for this draft

  - **`mastodon_post_published_at` (required)**

    `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

  - **`private_url` (required)**

    `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

  - **`published_at` (required)**

    `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published anywhere.

  - **`social_set_id` (required)**

    `integer` — ID of the social set (account) this draft belongs to

  - **`status` (required)**

    `string` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. Note: this reflects the stored draft lifecycle and does not flip to a 'publishing' value while an immediate publish is in flight - fetch the draft detail endpoint and read its \`publish\_state\` to track that.

  - **`substack_post_enabled` (required)**

    `boolean` — Whether posting to Substack is enabled

  - **`substack_post_published_at` (required)**

    `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

  - **`threads_post_enabled` (required)**

    `boolean` — Whether posting to Threads is enabled

  - **`threads_post_published_at` (required)**

    `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

  - **`updated_at` (required)**

    `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

  - **`x_post_enabled` (required)**

    `boolean` — Whether posting to X is enabled

  - **`bluesky_published_url`**

    `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

  - **`draft_title`**

    `object` — Internal title for the draft. Null if not set.

  - **`linkedin_published_url`**

    `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

  - **`mastodon_published_url`**

    `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

  - **`preview`**

    `object` — Preview of the draft content (may be null for empty drafts).

  - **`scheduled_date`**

    `object` — Scheduled or planned datetime in UTC (ISO 8601); for planned drafts the date is inert. Null if the draft has no date.

  - **`share_url`**

    `object` — Public share URL for the draft. Null if the draft is not publicly shared.

  - **`substack_published_url`**

    `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

  - **`tags`**

    `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

    **Items:**

    `string`

  - **`threads_published_url`**

    `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

  - **`x_post_published_at`**

    `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

  - **`x_published_url`**

    `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

**Example:**

```json
{
  "results": [
    {
      "id": 12345,
      "preview": "Hello world",
      "scheduled_date": "2025-01-20T14:00:00Z",
      "draft_title": "My launch thread",
      "mastodon_post_enabled": true,
      "social_set_id": 67890,
      "share_url": "https://typefully.com/share/abc123",
      "private_url": "https://typefully.com/?d=12345&a=67890",
      "status": "",
      "tags": [
        "marketing",
        "product"
      ],
      "created_at": "2025-01-15T10:30:00Z",
      "updated_at": "2025-01-16T09:15:00Z",
      "published_at": "2025-01-20T14:00:05Z",
      "mastodon_post_published_at": "2025-01-20T14:00:05Z",
      "linkedin_post_published_at": "2025-01-20T14:00:05Z",
      "threads_post_published_at": "2025-01-20T14:00:05Z",
      "bluesky_post_published_at": "2025-01-20T14:00:05Z",
      "substack_post_published_at": "2025-01-20T14:00:05Z",
      "x_post_published_at": "2025-01-20T14:00:05Z",
      "x_post_enabled": true,
      "linkedin_post_enabled": true,
      "threads_post_enabled": true,
      "bluesky_post_enabled": true,
      "substack_post_enabled": true,
      "x_published_url": "https://x.com/username/status/1234567890",
      "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
      "mastodon_published_url": "https://mastodon.social/@username/1234567890",
      "threads_published_url": "https://www.threads.net/@username/post/ABC123",
      "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
      "substack_published_url": "https://substack.com/@username/note/c-123456789"
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Create draft

- **Method:** `POST`
- **Path:** `/v2/social-sets/{social_set_id}/drafts`
- **Tags:** Drafts

Create a new draft with content for one or more social media platforms. The draft can be saved as a draft, planned (`plan_at`: dated but inert - it never auto-publishes until confirmed), scheduled for later publishing, or published immediately.

**Account-level settings:** This endpoint automatically applies the following account-level settings if enabled: Auto-Retweet, Auto-Plug, and Natural Posting Time.

**Required permission:** WRITE access to create drafts (planning included - it creates no publishing commitment). PUBLISH access is required to schedule or publish immediately.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

#### Request Body

##### Content-Type: application/json

- **`platforms` (required)**

  `object` — Platform configurations for each social media platform

  - **`bluesky`**

    `object` — Bluesky configuration

  - **`linkedin`**

    `object` — LinkedIn configuration

  - **`mastodon`**

    `object` — Mastodon configuration

  - **`substack`**

    `object` — Substack Notes configuration

  - **`threads`**

    `object` — Threads configuration

  - **`x`**

    `object` — X (Twitter) configuration

  - **`x_article`**

    `object` — X Article configuration. This platform is standalone: do not combine it with other platforms.

- **`draft_title`**

  `object` — Draft title, for internal organization only; not posted to social media.

- **`plan_at`**

  `object` — When to plan the draft. A planned draft is dated but inert: it shows on the queue and calendar at its date but never auto-publishes until confirmed by later setting \`publish\_at\`. Accepts "next-free-slot" or a future ISO 8601 datetime with timezone ("now" is not valid). Mutually exclusive with \`publish\_at\`. Omit to save as a plain draft.

- **`publish_at`**

  `object` — When to publish: "now" (immediate), "next-free-slot" (next available posting slot), or a future ISO 8601 datetime with timezone. Omit to save as a draft. Mutually exclusive with \`plan\_at\`. "now" is asynchronous: the response returns \`publish\_state\`="in\_progress" while \`status\` stays "draft" and published URLs are null - success, not failure; poll GET /drafts/{id} until \`publish\_state\`="finished", then read \`status\` and the published URLs.

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped.

- **`share`**

  `boolean`, default: `false` — Whether to generate a public share URL for this draft. When true, anyone with the URL can view the draft content.

- **`tags`**

  `array` — Tag slugs (not names). Tags must already exist in the social set - list them via /tags.

  **Items:**

  `string`

**Example:**

```json
{
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "draft_title": "Weekly Newsletter",
  "scratchpad_text": "line 1\nline 2\n\nline 4",
  "tags": [
    "marketing",
    "product"
  ],
  "share": false,
  "publish_at": "2027-12-20T09:00:00-05:00",
  "plan_at": "2027-12-20T09:00:00-05:00"
}
```

#### Responses

##### Status: 201 Created

###### Content-Type: application/json

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

- **`draft_id` (required)**

  `integer` — Deprecated: Use 'id' instead. Unique identifier for the draft.

- **`id` (required)**

  `integer` — Unique identifier for the draft

- **`platforms` (required)**

  `object` — Platform configurations showing which platforms are enabled and their content

  - **`bluesky`**

    `object` — Bluesky configuration

  - **`linkedin`**

    `object` — LinkedIn configuration

  - **`mastodon`**

    `object` — Mastodon configuration

  - **`substack`**

    `object` — Substack Notes configuration

  - **`threads`**

    `object` — Threads configuration

  - **`x`**

    `object` — X (Twitter) configuration

  - **`x_article`**

    `object` — X Article configuration

- **`preview` (required)**

  `string` — Text preview of the draft, smart-trimmed with a 100-character limit

- **`private_url` (required)**

  `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this draft belongs to

- **`status` (required)**

  `string`, possible values: `"draft", "scheduled", "published", "publishing", "error", "planned"` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. This reflects the stored draft lifecycle; it does not flip to 'publishing' while an immediate publish is in flight - use \`publish\_state\` to track that.

- **`bluesky_post_published_at`**

  `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

- **`bluesky_published_url`**

  `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

- **`draft_title`**

  `object` — Human-readable title for the draft. This is for internal organization only and is not posted to social media.

- **`linkedin_post_published_at`**

  `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

- **`linkedin_published_url`**

  `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

- **`mastodon_post_published_at`**

  `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

- **`mastodon_published_url`**

  `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

- **`publish_state`**

  `object` — Async publish-progress signal, separate from \`status\`. null = no publish initiated; 'in\_progress' = at least one platform is currently being posted; 'finished' = publishing has completed for all platforms. 'finished' means the job is done, not that it succeeded - read \`status\` and the per-platform published URLs (x\_published\_url, etc.) for the outcome. After publish\_at="now", poll GET /drafts/{id} until publish\_state is 'finished'.

- **`published_at`**

  `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published.

- **`scheduled_date`**

  `object` — Timestamp when the draft is scheduled to publish - or, for planned drafts, the inert planned date (ISO 8601 in UTC). Null if the draft has no date.

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped.

- **`share_url`**

  `object` — Public share URL for the draft. Null if the draft is not publicly shared.

- **`substack_post_published_at`**

  `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

- **`substack_published_url`**

  `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

- **`tags`**

  `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

  **Items:**

  `string`

- **`threads_post_published_at`**

  `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

- **`threads_published_url`**

  `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

- **`updated_at`**

  `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

- **`x_article_published_at`**

  `object` — Timestamp when the X Article was published (ISO 8601 format in UTC). Null if not published.

- **`x_article_published_url`**

  `object` — URL of the published X Article. Null if not published or URL not available.

- **`x_post_published_at`**

  `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

- **`x_published_url`**

  `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

**Example:**

```json
{
  "id": 12345,
  "social_set_id": 67890,
  "status": "draft",
  "publish_state": null,
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-16T09:15:00Z",
  "scheduled_date": "2025-01-20T14:00:00Z",
  "published_at": "2025-01-20T14:00:05Z",
  "draft_title": "Weekly Newsletter",
  "tags": [
    "marketing",
    "product"
  ],
  "preview": "Excited to announce our new feature! 🚀",
  "share_url": "https://typefully.com/share/abc123",
  "private_url": "https://typefully.com/?d=12345&a=67890",
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "x_published_url": "https://x.com/username/status/1234567890",
  "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
  "mastodon_published_url": "https://mastodon.social/@username/1234567890",
  "threads_published_url": "https://www.threads.net/@username/post/ABC123",
  "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
  "substack_published_url": "https://substack.com/@username/note/c-123456789",
  "x_article_published_url": "https://x.com/i/article/1234567890",
  "x_post_published_at": "2025-01-20T14:00:05Z",
  "linkedin_post_published_at": "2025-01-20T14:00:08Z",
  "mastodon_post_published_at": "2025-01-20T14:00:10Z",
  "threads_post_published_at": "2025-01-20T14:00:12Z",
  "bluesky_post_published_at": "2025-01-20T14:00:15Z",
  "substack_post_published_at": "2025-01-20T14:00:15Z",
  "x_article_published_at": "2025-01-20T14:00:15Z",
  "scratchpad_text": "line 1\nline 2\n\nline 4"
}
```

##### Status: 400 Invalid request data or validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 402 Account is paused or requires a paid plan before creating drafts

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions or feature not available

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Get draft

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}`
- **Tags:** Drafts

Retrieve a specific draft by ID, including its content for all configured platforms, status, and scheduling information.

If the draft has comment threads, the response includes Typefully comment-thread markers in `posts[*].text` and, for X Articles, in `platforms.x_article.content_markdown`. These markers are structural anchor metadata for `GET → modify → PATCH` round-trips; preserve them exactly when editing.

For read-only display/export, pass `?exclude_comment_markers=true` to render draft text without markers. Content returned with that flag set should not be PATCHed back unless you intend to resolve or remove comment anchors.

**Draft statuses:** 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed.

**Required permission:** READ access to this social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `exclude_comment_markers`

- **In:** `query`

When true, render `posts[*].text` as plain user-visible text without `<typ:comment-thread>` markers, and render X Article `content_markdown` without comment markers. Use only for read-only flows (LLM context windows, exports). The default (false) emits markers so a round-trip back to PATCH preserves comment anchors.

`boolean`, default: `false` — When true, render \`posts\[\*].text\` as plain user-visible text without \`\<typ:comment-thread>\` markers, and render X Article \`content\_markdown\` without comment markers. Use only for read-only flows (LLM context windows, exports). The default (false) emits markers so a round-trip back to PATCH preserves comment anchors.

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

- **`draft_id` (required)**

  `integer` — Deprecated: Use 'id' instead. Unique identifier for the draft.

- **`id` (required)**

  `integer` — Unique identifier for the draft

- **`platforms` (required)**

  `object` — Platform configurations showing which platforms are enabled and their content

  - **`bluesky`**

    `object` — Bluesky configuration

  - **`linkedin`**

    `object` — LinkedIn configuration

  - **`mastodon`**

    `object` — Mastodon configuration

  - **`substack`**

    `object` — Substack Notes configuration

  - **`threads`**

    `object` — Threads configuration

  - **`x`**

    `object` — X (Twitter) configuration

  - **`x_article`**

    `object` — X Article configuration

- **`preview` (required)**

  `string` — Text preview of the draft, smart-trimmed with a 100-character limit

- **`private_url` (required)**

  `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this draft belongs to

- **`status` (required)**

  `string`, possible values: `"draft", "scheduled", "published", "publishing", "error", "planned"` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. This reflects the stored draft lifecycle; it does not flip to 'publishing' while an immediate publish is in flight - use \`publish\_state\` to track that.

- **`bluesky_post_published_at`**

  `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

- **`bluesky_published_url`**

  `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

- **`draft_title`**

  `object` — Human-readable title for the draft. This is for internal organization only and is not posted to social media.

- **`linkedin_post_published_at`**

  `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

- **`linkedin_published_url`**

  `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

- **`mastodon_post_published_at`**

  `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

- **`mastodon_published_url`**

  `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

- **`publish_state`**

  `object` — Async publish-progress signal, separate from \`status\`. null = no publish initiated; 'in\_progress' = at least one platform is currently being posted; 'finished' = publishing has completed for all platforms. 'finished' means the job is done, not that it succeeded - read \`status\` and the per-platform published URLs (x\_published\_url, etc.) for the outcome. After publish\_at="now", poll GET /drafts/{id} until publish\_state is 'finished'.

- **`published_at`**

  `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published.

- **`scheduled_date`**

  `object` — Timestamp when the draft is scheduled to publish - or, for planned drafts, the inert planned date (ISO 8601 in UTC). Null if the draft has no date.

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped.

- **`share_url`**

  `object` — Public share URL for the draft. Null if the draft is not publicly shared.

- **`substack_post_published_at`**

  `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

- **`substack_published_url`**

  `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

- **`tags`**

  `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

  **Items:**

  `string`

- **`threads_post_published_at`**

  `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

- **`threads_published_url`**

  `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

- **`updated_at`**

  `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

- **`x_article_published_at`**

  `object` — Timestamp when the X Article was published (ISO 8601 format in UTC). Null if not published.

- **`x_article_published_url`**

  `object` — URL of the published X Article. Null if not published or URL not available.

- **`x_post_published_at`**

  `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

- **`x_published_url`**

  `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

**Example:**

```json
{
  "id": 12345,
  "social_set_id": 67890,
  "status": "draft",
  "publish_state": null,
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-16T09:15:00Z",
  "scheduled_date": "2025-01-20T14:00:00Z",
  "published_at": "2025-01-20T14:00:05Z",
  "draft_title": "Weekly Newsletter",
  "tags": [
    "marketing",
    "product"
  ],
  "preview": "Excited to announce our new feature! 🚀",
  "share_url": "https://typefully.com/share/abc123",
  "private_url": "https://typefully.com/?d=12345&a=67890",
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "x_published_url": "https://x.com/username/status/1234567890",
  "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
  "mastodon_published_url": "https://mastodon.social/@username/1234567890",
  "threads_published_url": "https://www.threads.net/@username/post/ABC123",
  "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
  "substack_published_url": "https://substack.com/@username/note/c-123456789",
  "x_article_published_url": "https://x.com/i/article/1234567890",
  "x_post_published_at": "2025-01-20T14:00:05Z",
  "linkedin_post_published_at": "2025-01-20T14:00:08Z",
  "mastodon_post_published_at": "2025-01-20T14:00:10Z",
  "threads_post_published_at": "2025-01-20T14:00:12Z",
  "bluesky_post_published_at": "2025-01-20T14:00:15Z",
  "substack_post_published_at": "2025-01-20T14:00:15Z",
  "x_article_published_at": "2025-01-20T14:00:15Z",
  "scratchpad_text": "line 1\nline 2\n\nline 4"
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Update draft

- **Method:** `PATCH`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}`
- **Tags:** Drafts

Update an existing draft with partial update semantics. Only provided fields are updated; omitted fields remain unchanged. Scheduled drafts require publish access to edit.

## Planning

A planned draft is dated but inert: it has a `scheduled_date` but never auto-publishes until confirmed. Use `plan_at` and `publish_at` (mutually exclusive per request) to move a draft through the lifecycle:

- `plan_at=<datetime|"next-free-slot">` on a plain draft plans it, on a planned draft moves the date (both write access), and on a scheduled draft unschedules it into a plan (publish access - it disarms a live schedule).
- `publish_at=<datetime|"next-free-slot">` on a planned draft confirms it into a real schedule (publish access; echo the draft's `scheduled_date` to confirm at the planned date). `publish_at="now"` publishes it immediately.
- An explicit `plan_at=null` or `publish_at=null` clears the date and returns the draft to plain draft status (write access from planned, publish access from scheduled).
- Content edits on a planned draft require write access only.

## Note about Comment-thread markers

If the draft has comment threads, submitted `posts[*].text` and X Article `platforms.x_article.content_markdown` must preserve the Typefully comment-thread markers received from `GET /drafts/{id}`. Validation is platform-level: every comment thread anchored on a platform must appear somewhere in that platform's submitted text.

Recommended edit flow: GET the draft without `exclude_comment_markers`, modify text while preserving markers exactly, then PATCH with `force_overwrite_comments: false` (the default).

- `409 COMMENTS_MARKER_MISMATCH` will be thrown if an expected comment thread marker is missing unless `"force_overwrite_comments": true` is set, in which case the affected threads are resolved server-side.
- `400 COMMENTS_MARKER_UNKNOWN_ID` will be thrown if you submit an id that doesn't exist on this draft. - `400 COMMENTS_MARKER_MALFORMED` will be thrown if the marker tag is malformed (bad UUID, unbalanced, attribute violations, etc.).

Pass `?exclude_comment_markers=true` to render the response text without markers (read-only / display rendering — does NOT skip server-side marker validation on the request body). Do not PATCH content returned with that flag unless you intend to resolve or remove comment anchors.

**Required permission:** WRITE access to edit drafts. PUBLISH access is required to edit scheduled drafts, schedule, or publish.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `exclude_comment_markers`

- **In:** `query`

Render the response's `posts[*].text` as plain text without `<typ:comment-thread>` markers, and render X Article `content_markdown` without comment markers. Render-only — does not affect request-body validation.

`boolean`, default: `false` — Render the response's \`posts\[\*].text\` as plain text without \`\<typ:comment-thread>\` markers, and render X Article \`content\_markdown\` without comment markers. Render-only — does not affect request-body validation.

#### Request Body

##### Content-Type: application/json

- **`draft_title`**

  `object` — Draft title, for internal organization only; not posted to social media. Omit to keep unchanged.

- **`force_overwrite_comments`**

  `boolean`, default: `false` — Comment-thread anchor preservation toggle. When false (the default), submitting \`posts\[\*].text\` or X Article \`content\_markdown\` whose \`\<typ:comment-thread>\` markers don't match the draft's stored comment threads is rejected with \`409 COMMENTS\_MARKER\_MISMATCH\`; re-include the missing markers and retry. When true, missing markers are accepted: their comment threads are resolved server-side and their anchors stripped; submitted markers still validate normally. Only JSON \`true\`/\`false\` (not \`"true"\` strings).

- **`plan_at`**

  `object` — When to plan. A planned draft is dated but inert: on the queue and calendar but never auto-publishing until confirmed via \`publish\_at\`. A future datetime with timezone or "next-free-slot" plans a plain draft, replans a planned one, or unschedules a scheduled one into a plan (requires publish access; "now" invalid). Explicit null returns a planned or scheduled draft to plain draft status. Mutually exclusive with \`publish\_at\`. Omit to keep unchanged.

- **`platforms`**

  `object` — Platform configurations. Only provided platforms will be updated; omitted platforms remain unchanged.

- **`publish_at`**

  `object` — When to publish: "now" (immediate), "next-free-slot", or a future ISO 8601 datetime with timezone. On a planned draft, a datetime or "next-free-slot" confirms it into a real schedule (echo its \`scheduled\_date\` to keep the date); "now" publishes it immediately. Explicit null returns it to plain draft status. Mutually exclusive with \`plan\_at\`. Omit to keep unchanged. "now" is asynchronous: the response returns \`publish\_state\`="in\_progress" (success, not failure); poll GET /drafts/{id} until "finished".

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped. Omit to keep unchanged.

- **`share`**

  `object` — Whether to generate a public share URL. Omit to keep unchanged.

- **`tags`**

  `object` — Tag slugs (not names). Tags must already exist in the social set - list them via /tags. Omit to keep unchanged.

**Example:**

```json
{
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "draft_title": "Weekly Newsletter",
  "scratchpad_text": "line 1\nline 2\n\nline 4",
  "tags": [
    "marketing",
    "product"
  ],
  "share": true,
  "publish_at": "2027-12-20T09:00:00-05:00",
  "plan_at": "2027-12-20T09:00:00-05:00",
  "force_overwrite_comments": false
}
```

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

- **`draft_id` (required)**

  `integer` — Deprecated: Use 'id' instead. Unique identifier for the draft.

- **`id` (required)**

  `integer` — Unique identifier for the draft

- **`platforms` (required)**

  `object` — Platform configurations showing which platforms are enabled and their content

  - **`bluesky`**

    `object` — Bluesky configuration

  - **`linkedin`**

    `object` — LinkedIn configuration

  - **`mastodon`**

    `object` — Mastodon configuration

  - **`substack`**

    `object` — Substack Notes configuration

  - **`threads`**

    `object` — Threads configuration

  - **`x`**

    `object` — X (Twitter) configuration

  - **`x_article`**

    `object` — X Article configuration

- **`preview` (required)**

  `string` — Text preview of the draft, smart-trimmed with a 100-character limit

- **`private_url` (required)**

  `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this draft belongs to

- **`status` (required)**

  `string`, possible values: `"draft", "scheduled", "published", "publishing", "error", "planned"` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. This reflects the stored draft lifecycle; it does not flip to 'publishing' while an immediate publish is in flight - use \`publish\_state\` to track that.

- **`bluesky_post_published_at`**

  `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

- **`bluesky_published_url`**

  `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

- **`draft_title`**

  `object` — Human-readable title for the draft. This is for internal organization only and is not posted to social media.

- **`linkedin_post_published_at`**

  `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

- **`linkedin_published_url`**

  `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

- **`mastodon_post_published_at`**

  `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

- **`mastodon_published_url`**

  `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

- **`publish_state`**

  `object` — Async publish-progress signal, separate from \`status\`. null = no publish initiated; 'in\_progress' = at least one platform is currently being posted; 'finished' = publishing has completed for all platforms. 'finished' means the job is done, not that it succeeded - read \`status\` and the per-platform published URLs (x\_published\_url, etc.) for the outcome. After publish\_at="now", poll GET /drafts/{id} until publish\_state is 'finished'.

- **`published_at`**

  `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published.

- **`scheduled_date`**

  `object` — Timestamp when the draft is scheduled to publish - or, for planned drafts, the inert planned date (ISO 8601 in UTC). Null if the draft has no date.

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped.

- **`share_url`**

  `object` — Public share URL for the draft. Null if the draft is not publicly shared.

- **`substack_post_published_at`**

  `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

- **`substack_published_url`**

  `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

- **`tags`**

  `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

  **Items:**

  `string`

- **`threads_post_published_at`**

  `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

- **`threads_published_url`**

  `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

- **`updated_at`**

  `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

- **`x_article_published_at`**

  `object` — Timestamp when the X Article was published (ISO 8601 format in UTC). Null if not published.

- **`x_article_published_url`**

  `object` — URL of the published X Article. Null if not published or URL not available.

- **`x_post_published_at`**

  `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

- **`x_published_url`**

  `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

**Example:**

```json
{
  "id": 12345,
  "social_set_id": 67890,
  "status": "draft",
  "publish_state": null,
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-16T09:15:00Z",
  "scheduled_date": "2025-01-20T14:00:00Z",
  "published_at": "2025-01-20T14:00:05Z",
  "draft_title": "Weekly Newsletter",
  "tags": [
    "marketing",
    "product"
  ],
  "preview": "Excited to announce our new feature! 🚀",
  "share_url": "https://typefully.com/share/abc123",
  "private_url": "https://typefully.com/?d=12345&a=67890",
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "x_published_url": "https://x.com/username/status/1234567890",
  "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
  "mastodon_published_url": "https://mastodon.social/@username/1234567890",
  "threads_published_url": "https://www.threads.net/@username/post/ABC123",
  "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
  "substack_published_url": "https://substack.com/@username/note/c-123456789",
  "x_article_published_url": "https://x.com/i/article/1234567890",
  "x_post_published_at": "2025-01-20T14:00:05Z",
  "linkedin_post_published_at": "2025-01-20T14:00:08Z",
  "mastodon_post_published_at": "2025-01-20T14:00:10Z",
  "threads_post_published_at": "2025-01-20T14:00:12Z",
  "bluesky_post_published_at": "2025-01-20T14:00:15Z",
  "substack_post_published_at": "2025-01-20T14:00:15Z",
  "x_article_published_at": "2025-01-20T14:00:15Z",
  "scratchpad_text": "line 1\nline 2\n\nline 4"
}
```

##### Status: 400 Invalid request data or draft cannot be edited (e.g., already published)

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 402 Account is paused or requires a paid plan before editing drafts

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions or feature not available

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Delete draft

- **Method:** `DELETE`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}`
- **Tags:** Drafts

Delete a draft. Requires WRITE access. You can delete your own drafts in any status (DRAFT, ERROR, SCHEDULED, PUBLISHED, PUBLISHING) with WRITE access. Drafts created by other users also require WRITE access to delete.

**Required permission:** WRITE access to this social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

#### Responses

##### Status: 204 No Content

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Create media upload

- **Method:** `POST`
- **Path:** `/v2/social-sets/{social_set_id}/media/upload`
- **Tags:** Media

Generate a presigned S3 upload URL for images, videos, GIFs, or PDFs. After you receive the URL, upload the file contents with a PUT request and then reference the returned media\_id when creating drafts.

**Uploading:** Send a plain PUT with only raw file bytes as the body — no extra headers (`Content-Type`, `Authorization`, etc.). The presigned URL signature was calculated without them, so adding headers causes a `403 SignatureDoesNotMatch`. Use `curl -T <file>` (not `--data-binary`), `requests.put(url, data=file_bytes)` in Python, or `fetch(url, {method:'PUT', body:buffer})` in JS. A successful upload returns `200` or `204`.

**Required permission:** WRITE access to the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

#### Request Body

##### Content-Type: application/json

- **`file_name` (required)**

  `string` — Original filename with extension (e.g., 'image.jpg', 'video.mp4'). Used for MIME type detection and display. Allowed characters: letters, numbers, hyphens, underscores, periods, parentheses. Allowed extensions: .jpg, .jpeg, .png, .webp, .gif, .mp4, .mov, .pdf

- **`alt_text`**

  `object` — Accessibility description for the media, used as alt text when publishing. Can also be set later with PATCH /media/{media\_id}. Platform-specific character limits are validated when the media is attached to a draft.

**Example:**

```json
{
  "file_name": "profile-photo.jpg",
  "alt_text": "A golden retriever catching a frisbee mid-air in a sunny park"
}
```

#### Responses

##### Status: 201 Upload URL created successfully

###### Content-Type: application/json

- **`media_id` (required)**

  `string`, format: `uuid` — Unique identifier for the uploaded media. Use this ID when attaching media to posts.

- **`upload_url` (required)**

  `string` — Presigned S3 URL for uploading the file. PUT the file content to this URL within 1 hour.

**Example:**

```json
{
  "media_id": "550e8400-e29b-41d4-a716-446655440000",
  "upload_url": "https://s3.amazonaws.com/bucket/path/file?signature=..."
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Get media status

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/media/{media_id}`
- **Tags:** Media

Retrieves the processing status of an uploaded media file. Poll this endpoint after uploading to check when the file is ready to use in drafts.

If no file is received before the upload URL expires (1 hour), the media transitions to 'failed' — create a new media upload and try again.

**Required permission:** READ access to the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `media_id` required

- **In:** `path`

`string`, format: `uuid`

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`file_name` (required)**

  `string` — Original filename

- **`media_id` (required)**

  `string`, format: `uuid` — Unique identifier for the media file

- **`status` (required)**

  `string`, possible values: `"processing", "ready", "failed"` — Processing status: 'processing' = file is being processed, 'ready' = file is ready to use in posts, 'failed' = processing failed

- **`alt_text`**

  `object` — Accessibility description for the media, used as alt text when publishing. Omitted when not set.

- **`error_reason`**

  `object` — Error message if status is 'failed'. Null otherwise.

- **`media_urls`**

  `object` — Dictionary of media URLs. For resizable images (PNG, JPG, WEBP): includes 'small' (600px), 'medium' (1200px), 'large' (3200px), and 'original'. For non-resizable media (GIF, video, PDF): includes only 'original'.

- **`mime`**

  `object` — MIME type of the media file

**Example:**

```json
{
  "media_id": "",
  "file_name": "photo.jpg",
  "mime": "image/jpeg",
  "status": "ready",
  "error_reason": "Unsupported file format",
  "alt_text": "A golden retriever catching a frisbee mid-air in a sunny park",
  "media_urls": {
    "large": "https://cdn.../resized/...",
    "medium": "https://cdn.../resized/...",
    "original": "https://cdn.../original/...",
    "small": "https://cdn.../resized/..."
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Update media

- **Method:** `PATCH`
- **Path:** `/v2/social-sets/{social_set_id}/media/{media_id}`
- **Tags:** Media

Updates metadata of an uploaded media file. Currently supports setting or clearing `alt_text`.

Alt text is copied into a draft when the media is attached (draft create or edit), so set it **before** referencing the media in a draft. To reliably change alt text on media already attached to a draft, update the media and then re-send the draft content with a draft PATCH. (Some platforms fall back to the media's current alt text when the draft snapshot has none, but only re-sending the draft content updates it everywhere.)

**Required permission:** WRITE access to the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `media_id` required

- **In:** `path`

`string`, format: `uuid`

#### Request Body

##### Content-Type: application/json

- **`alt_text`**

  `object` — Accessibility description for the media, used as alt text when publishing. Pass null to clear it. If omitted, the current value is kept.

**Example:**

```json
{
  "alt_text": "A golden retriever catching a frisbee mid-air in a sunny park"
}
```

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`file_name` (required)**

  `string` — Original filename

- **`media_id` (required)**

  `string`, format: `uuid` — Unique identifier for the media file

- **`status` (required)**

  `string`, possible values: `"processing", "ready", "failed"` — Processing status: 'processing' = file is being processed, 'ready' = file is ready to use in posts, 'failed' = processing failed

- **`alt_text`**

  `object` — Accessibility description for the media, used as alt text when publishing. Omitted when not set.

- **`error_reason`**

  `object` — Error message if status is 'failed'. Null otherwise.

- **`media_urls`**

  `object` — Dictionary of media URLs. For resizable images (PNG, JPG, WEBP): includes 'small' (600px), 'medium' (1200px), 'large' (3200px), and 'original'. For non-resizable media (GIF, video, PDF): includes only 'original'.

- **`mime`**

  `object` — MIME type of the media file

**Example:**

```json
{
  "media_id": "",
  "file_name": "photo.jpg",
  "mime": "image/jpeg",
  "status": "ready",
  "error_reason": "Unsupported file format",
  "alt_text": "A golden retriever catching a frisbee mid-air in a sunny park",
  "media_urls": {
    "large": "https://cdn.../resized/...",
    "medium": "https://cdn.../resized/...",
    "original": "https://cdn.../original/...",
    "small": "https://cdn.../resized/..."
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### List tags

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/tags`
- **Tags:** Tags

Retrieve all tags for a social set, ordered by their slugs.

**Required permission:** READ access to the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `limit`

- **In:** `query`

Maximum number of items to return per page

**Any of:**

`integer`

`null`

##### `offset`

- **In:** `query`

Number of items to skip from the beginning

`integer`, default: `0` — Number of items to skip from the beginning

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`count` (required)**

  `integer`

- **`limit` (required)**

  `integer`

- **`next` (required)**

  `object`

- **`offset` (required)**

  `integer`

- **`previous` (required)**

  `object`

- **`results` (required)**

  `array`

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the tag was created (ISO 8601 format in UTC)

  - **`name` (required)**

    `string` — Display name for the tag

  - **`slug` (required)**

    `string` — Auto-generated URL-safe identifier from the tag name

**Example:**

```json
{
  "results": [
    {
      "slug": "marketing",
      "name": "Marketing",
      "created_at": "2025-01-15T10:30:00Z"
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Create tag

- **Method:** `POST`
- **Path:** `/v2/social-sets/{social_set_id}/tags`
- **Tags:** Tags

Create a new tag for a social set. The slug is automatically generated from the tag name, which must be unique per social set.

**Required permission:** WRITE access to the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

#### Request Body

##### Content-Type: application/json

- **`name` (required)**

  `string` — Display name for the tag. The slug will be auto-generated from this name.

**Example:**

```json
{
  "name": "Marketing"
}
```

#### Responses

##### Status: 201 Created

###### Content-Type: application/json

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the tag was created (ISO 8601 format in UTC)

- **`name` (required)**

  `string` — Display name for the tag

- **`slug` (required)**

  `string` — Auto-generated URL-safe identifier from the tag name

**Example:**

```json
{
  "slug": "marketing",
  "name": "Marketing",
  "created_at": "2025-01-15T10:30:00Z"
}
```

##### Status: 400 Invalid request data or validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions or feature not available

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Get queue schedule

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/queue/schedule`
- **Tags:** Queue

Retrieve the queue schedule rules for a social set.

**Required permission:** READ access to this social set.

Behavior:

- If the schedule row does not exist yet, it is created with defaults.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

#### Responses

##### Status: 200 Queue schedule rules for this social set.

###### Content-Type: application/json

- **`rules` (required)**

  `array` — Schedule rules in local time.

  **Items:**

  - **`days` (required)**

    `array` — Days of week this rule applies to

    **Items:**

    `string`, possible values: `"mon", "tue", "wed", "thu", "fri", "sat", "sun"`

  - **`h` (required)**

    `integer` — Hour in 24h clock (0-23)

  - **`m` (required)**

    `integer` — Minute (0-59)

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this schedule belongs to.

- **`timezone` (required)**

  `string` — Timezone name for this social set.

**Example:**

```json
{
  "social_set_id": 123,
  "timezone": "America/New_York",
  "rules": [
    {
      "h": 0,
      "m": 0,
      "days": [
        "mon",
        "tue",
        "wed",
        "thu",
        "fri"
      ]
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Replace queue schedule

- **Method:** `PUT`
- **Path:** `/v2/social-sets/{social_set_id}/queue/schedule`
- **Tags:** Queue

Replace the queue schedule rules for a social set.

**Required permission:** ADMIN access to this social set. Semantics: full replacement (atomic).

Rule validation:

- `h` in `0..23`, `m` in `0..59`
- `days` values are one of: `mon,tue,wed,thu,fri,sat,sun`
- Duplicate day+time combinations are rejected

Note: `rules=[]` is allowed and represents an empty schedule.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

#### Request Body

##### Content-Type: application/json

- **`rules` (required)**

  `array` — New schedule rules (full replacement).

  **Items:**

  - **`days` (required)**

    `array` — Days of week this rule applies to

    **Items:**

    `string`, possible values: `"mon", "tue", "wed", "thu", "fri", "sat", "sun"`

  - **`h` (required)**

    `integer` — Hour in 24h clock (0-23)

  - **`m` (required)**

    `integer` — Minute (0-59)

**Example:**

```json
{
  "rules": [
    {
      "h": 0,
      "m": 0,
      "days": [
        "mon",
        "tue",
        "wed",
        "thu",
        "fri"
      ]
    }
  ]
}
```

#### Responses

##### Status: 200 Updated queue schedule rules.

###### Content-Type: application/json

- **`rules` (required)**

  `array` — Schedule rules in local time.

  **Items:**

  - **`days` (required)**

    `array` — Days of week this rule applies to

    **Items:**

    `string`, possible values: `"mon", "tue", "wed", "thu", "fri", "sat", "sun"`

  - **`h` (required)**

    `integer` — Hour in 24h clock (0-23)

  - **`m` (required)**

    `integer` — Minute (0-59)

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this schedule belongs to.

- **`timezone` (required)**

  `string` — Timezone name for this social set.

**Example:**

```json
{
  "social_set_id": 123,
  "timezone": "America/New_York",
  "rules": [
    {
      "h": 0,
      "m": 0,
      "days": [
        "mon",
        "tue",
        "wed",
        "thu",
        "fri"
      ]
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Get queue

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/queue`
- **Tags:** Queue

Retrieve queue slots and the scheduled AND planned drafts occupying them, between `start_date` and `end_date` (inclusive). Check each draft's `status`: 'scheduled' = will auto-publish at its `scheduled_date`; 'planned' = dated but inert - it will NOT auto-publish until confirmed (a past planned date is not overdue and not a failure).

**Required permission:** READ access to this social set.

Notes:

- `start_date` and `end_date` are interpreted in the social set timezone.
- Ranges larger than 62 days are rejected.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `start_date` required

- **In:** `query`

`string`

##### `end_date` required

- **In:** `query`

`string`

#### Responses

##### Status: 200 Queue view (slots + scheduled drafts) for a date range.

###### Content-Type: application/json

- **`days` (required)**

  `array` — Days in the requested date range (inclusive).

  **Items:**

  - **`date` (required)**

    `string` — YYYY-MM-DD in the social set timezone

  - **`items` (required)**

    `array` — Queue items for this day (in chronological order).

    **Items:**

    - **`at` (required)**

      `string`, format: `date-time` — ISO8601 datetime in UTC.

    - **`kind` (required)**

      `string`, possible values: `"queue_slot", "custom_time"` — Queue item kind. - \`queue\_slot\`: schedule-generated queue slot time (free slot if \`draft\` is null) - \`custom\_time\`: scheduled or planned draft that does not count as a slot occupant (custom-time scheduling, or collision overflow)

    - **`draft`**

      `object` — Scheduled or planned draft occupying this time (check \`status\` to tell them apart). Null means the slot is free.

- **`end_date` (required)**

  `string` — YYYY-MM-DD in the social set timezone

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this queue belongs to.

- **`start_date` (required)**

  `string` — YYYY-MM-DD in the social set timezone

**Example:**

```json
{
  "social_set_id": 123,
  "start_date": "2026-02-01",
  "end_date": "2026-02-29",
  "days": [
    {
      "date": "",
      "items": []
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Resolve LinkedIn organization from URL

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/linkedin/organizations/resolve`
- **Tags:** Social Sets

Resolve a LinkedIn company/school URL into organization metadata that can be used to build LinkedIn mention syntax in post text.

This endpoint is resolver-only and is not a general organization search endpoint.

Mention format: `@[Company Name](urn:li:organization:123456)`

**Required permission:** READ access to the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `organization_url` required

- **In:** `query`

Public LinkedIn organization URL (company or school profile).

`string` — Public LinkedIn organization URL (company or school profile).

#### Responses

##### Status: 200 Resolved LinkedIn organization metadata

###### Content-Type: application/json

- **`id` (required)**

  `string` — LinkedIn organization ID

- **`mention_text` (required)**

  `string` — Ready-to-use LinkedIn mention syntax to paste into post text.

- **`urn` (required)**

  `string` — LinkedIn organization URN, usable in mention syntax

- **`description`**

  `object` — Organization description

- **`logo_url`**

  `object` — Organization logo URL

- **`name`**

  `object` — Organization display name

- **`url`**

  `object` — Public LinkedIn company URL

- **`vanity_name`**

  `object` — LinkedIn vanity name

- **`website`**

  `object` — Organization website

**Example:**

```json
{
  "id": "86779668",
  "urn": "urn:li:organization:86779668",
  "mention_text": "@[Typefully](urn:li:organization:86779668)",
  "name": "",
  "vanity_name": "",
  "description": "",
  "website": "",
  "logo_url": "",
  "url": ""
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 503 Service unavailable

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### List comment threads on a draft

- **Method:** `GET`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}/comment-threads`
- **Tags:** Comments

Retrieve comment threads attached to a draft, ordered by creation time. Each thread includes the original `selected_text` snapshot and the full ordered list of comments.

By default this endpoint returns only unresolved threads (`status=unresolved`). Use `status=resolved` to list resolved threads or `status=all` to list both unresolved and resolved threads. Example: `GET /v2/social-sets/4/drafts/12/comment-threads?status=all&limit=50`.

**Required permission:** READ access to this social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `platform`

- **In:** `query`

Optional platform filter.

**Any of:**

`string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"`

`null`

##### `status`

- **In:** `query`

Resolution filter. Defaults to `unresolved`, so resolved threads are omitted unless you request `resolved` or `all`.

`string`, possible values: `"unresolved", "resolved", "all"`, default: `"unresolved"` — Resolution filter. Defaults to \`unresolved\`, so resolved threads are omitted unless you request \`resolved\` or \`all\`.

##### `limit`

- **In:** `query`

`integer`, default: `10`

##### `offset`

- **In:** `query`

`integer`, default: `0`

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`count` (required)**

  `integer` — Total number of comment threads available.

- **`limit` (required)**

  `integer` — Items per page used for this request.

- **`offset` (required)**

  `integer` — Current offset value.

- **`results` (required)**

  `array` — Comment threads in the current page.

  **Items:**

  - **`comments` (required)**

    `array` — Comments in the thread, ordered by \`created\_at\`.

    **Items:**

    - **`created_at` (required)**

      `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

    - **`id` (required)**

      `string`, format: `uuid` — Unique identifier for the comment.

    - **`text` (required)**

      `string` — Plain-text comment body. Mentioning users is not supported.

    - **`user` (required)**

      `object` — Author of the comment.

      - **`id` (required)**

        `integer` — Unique identifier for the user.

      - **`name` (required)**

        `string` — Display name of the user.

      - **`profile_image_url`**

        `object` — URL to the user's profile image. Null if not available.

  - **`draft_id` (required)**

    `integer` — Identifier of the draft this comment thread belongs to.

  - **`id` (required)**

    `string`, format: `uuid` — Unique identifier for the comment thread.

  - **`platform` (required)**

    `string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"` — Platform the comment thread is anchored on.

  - **`status` (required)**

    `string`, possible values: `"unresolved", "resolved"` — Resolution status of the comment thread.

  - **`selected_text`**

    `object` — The text the comment thread was originally anchored to. Captured when the thread was created and not re-derived afterwards, so it may not match the current post text if the post has been edited. Null for threads that were created against a whole post rather than a specific span.

- **`next`**

  `object` — URL for the next page (null on last page).

- **`previous`**

  `object` — URL for the previous page (null on first page).

**Example:**

```json
{
  "results": [
    {
      "id": "",
      "draft_id": 1,
      "platform": "x",
      "status": "unresolved",
      "selected_text": "",
      "comments": []
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Create a comment thread

- **Method:** `POST`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}/comment-threads`
- **Tags:** Comments

Create a new comment thread on a draft. A comment thread is the anchored discussion container; comments are the messages inside the thread.

For post platforms (`x`, `linkedin`, `mastodon`, `threads`, `bluesky`, `substack`), provide `post_index` and anchor to a substring of that post's visible text using `selected_text`. Use `occurrence` to disambiguate when the same substring appears more than once; omit it to anchor on the first match.

For X Articles, send `platform: "x_article"` and omit `post_index` (or use 0). `selected_text` matches commentable article text in document order; Markdown syntax, media tags, X post embed tags, and fenced-code text are ignored. Code blocks do not support comment anchors. X Article text comments cannot overlap existing stored X Article anchors; unsupported or overlapping selections return `400 VALIDATION_ERROR`.

LinkedIn mentions appear in `posts[*].text` as `@[Name](urn:li:organization:ID)` or `@[Name](urn:li:person:ID)`. A mention is indivisible — `selected_text` must either contain the entire mention substring or stay outside it.

**Required permission:** WRITE access to this social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

#### Request Body

##### Content-Type: application/json

**Any of:**

- **`post_index` (required)**

  `integer` — Zero-based index of the target post within the platform's posts array.

- **`selected_text` (required)**

  `string` — Exact substring (codepoint-equal) of the target platform's visible flat text the comment thread is anchored to. Copy verbatim from the GET response. LinkedIn mentions appear inside \`posts\[\*].text\` as \`@\[Name]\(urn:li:organization:ID)\` or \`@\[Name]\(urn:li:person:ID)\`. Mentions are indivisible — \`selected\_text\` may either include the entire mention substring or stay outside it. A selection that starts or ends in the middle of a mention is rejected with \`400 VALIDATION\_ERROR\`.

- **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

- **`occurrence`**

  `integer`, default: `0` — Zero-based occurrence of \`selected\_text\` within the post when the same substring appears multiple times.

- **`platform`**

  `object` — Required when the draft has multiple commentable platforms; otherwise resolves to the source platform.

* **`platform` (required)**

  `string`, possible values: `"x_article"` — Use \`x\_article\` to anchor the comment thread on the draft article.

* **`selected_text` (required)**

  `string` — Exact substring (codepoint-equal) of the article's rendered visible text. Markdown syntax, media tags, and X post embed tags are not part of the match text. X Article text comments cannot overlap existing X Article text comments in this API version.

* **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

* **`occurrence`**

  `integer`, default: `0` — Zero-based occurrence of \`selected\_text\` within the article when the same substring appears multiple times.

* **`post_index`**

  `object` — Omit for X Article comments. If supplied with \`platform: "x\_article"\`, it must be 0.

**Example:**

```json
{
  "post_index": 0,
  "platform": "x",
  "selected_text": "",
  "occurrence": 0,
  "text": ""
}
```

#### Responses

##### Status: 201 Created

###### Content-Type: application/json

- **`comments` (required)**

  `array` — Comments in the thread, ordered by \`created\_at\`.

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

  - **`id` (required)**

    `string`, format: `uuid` — Unique identifier for the comment.

  - **`text` (required)**

    `string` — Plain-text comment body. Mentioning users is not supported.

  - **`user` (required)**

    `object` — Author of the comment.

    - **`id` (required)**

      `integer` — Unique identifier for the user.

    - **`name` (required)**

      `string` — Display name of the user.

    - **`profile_image_url`**

      `object` — URL to the user's profile image. Null if not available.

- **`draft_id` (required)**

  `integer` — Identifier of the draft this comment thread belongs to.

- **`id` (required)**

  `string`, format: `uuid` — Unique identifier for the comment thread.

- **`platform` (required)**

  `string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"` — Platform the comment thread is anchored on.

- **`status` (required)**

  `string`, possible values: `"unresolved", "resolved"` — Resolution status of the comment thread.

- **`selected_text`**

  `object` — The text the comment thread was originally anchored to. Captured when the thread was created and not re-derived afterwards, so it may not match the current post text if the post has been edited. Null for threads that were created against a whole post rather than a specific span.

**Example:**

```json
{
  "id": "",
  "draft_id": 1,
  "platform": "x",
  "status": "unresolved",
  "selected_text": "",
  "comments": [
    {
      "id": "",
      "text": "",
      "created_at": "",
      "user": null
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 409 Conflict

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Add a comment to an existing comment thread

- **Method:** `POST`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}/comment-threads/{comment_thread_id}/comments`
- **Tags:** Comments

Append a comment to an existing comment thread. Returns the full updated thread.

**Required permission:** WRITE access to this social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `comment_thread_id` required

- **In:** `path`

`string`, format: `uuid`

#### Request Body

##### Content-Type: application/json

- **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

**Example:**

```json
{
  "text": ""
}
```

#### Responses

##### Status: 201 Created

###### Content-Type: application/json

- **`comments` (required)**

  `array` — Comments in the thread, ordered by \`created\_at\`.

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

  - **`id` (required)**

    `string`, format: `uuid` — Unique identifier for the comment.

  - **`text` (required)**

    `string` — Plain-text comment body. Mentioning users is not supported.

  - **`user` (required)**

    `object` — Author of the comment.

    - **`id` (required)**

      `integer` — Unique identifier for the user.

    - **`name` (required)**

      `string` — Display name of the user.

    - **`profile_image_url`**

      `object` — URL to the user's profile image. Null if not available.

- **`draft_id` (required)**

  `integer` — Identifier of the draft this comment thread belongs to.

- **`id` (required)**

  `string`, format: `uuid` — Unique identifier for the comment thread.

- **`platform` (required)**

  `string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"` — Platform the comment thread is anchored on.

- **`status` (required)**

  `string`, possible values: `"unresolved", "resolved"` — Resolution status of the comment thread.

- **`selected_text`**

  `object` — The text the comment thread was originally anchored to. Captured when the thread was created and not re-derived afterwards, so it may not match the current post text if the post has been edited. Null for threads that were created against a whole post rather than a specific span.

**Example:**

```json
{
  "id": "",
  "draft_id": 1,
  "platform": "x",
  "status": "unresolved",
  "selected_text": "",
  "comments": [
    {
      "id": "",
      "text": "",
      "created_at": "",
      "user": null
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 409 Conflict

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Resolve a comment thread

- **Method:** `POST`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}/comment-threads/{comment_thread_id}/resolve`
- **Tags:** Comments

Resolves the comment thread and removes the corresponding comment markers from the text.

**Required permission:** Either authorship of the comment thread or WRITE access on the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `comment_thread_id` required

- **In:** `path`

`string`, format: `uuid`

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`comments` (required)**

  `array` — Comments in the thread, ordered by \`created\_at\`.

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

  - **`id` (required)**

    `string`, format: `uuid` — Unique identifier for the comment.

  - **`text` (required)**

    `string` — Plain-text comment body. Mentioning users is not supported.

  - **`user` (required)**

    `object` — Author of the comment.

    - **`id` (required)**

      `integer` — Unique identifier for the user.

    - **`name` (required)**

      `string` — Display name of the user.

    - **`profile_image_url`**

      `object` — URL to the user's profile image. Null if not available.

- **`draft_id` (required)**

  `integer` — Identifier of the draft this comment thread belongs to.

- **`id` (required)**

  `string`, format: `uuid` — Unique identifier for the comment thread.

- **`platform` (required)**

  `string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"` — Platform the comment thread is anchored on.

- **`status` (required)**

  `string`, possible values: `"unresolved", "resolved"` — Resolution status of the comment thread.

- **`selected_text`**

  `object` — The text the comment thread was originally anchored to. Captured when the thread was created and not re-derived afterwards, so it may not match the current post text if the post has been edited. Null for threads that were created against a whole post rather than a specific span.

**Example:**

```json
{
  "id": "",
  "draft_id": 1,
  "platform": "x",
  "status": "unresolved",
  "selected_text": "",
  "comments": [
    {
      "id": "",
      "text": "",
      "created_at": "",
      "user": null
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Update a comment's text

- **Method:** `PATCH`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}/comment-threads/{comment_thread_id}/comments/{comment_id}`
- **Tags:** Comments

Update the plain-text body of a single comment. Returns the full updated thread.

**Required permission:** Authorship of the comment.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `comment_thread_id` required

- **In:** `path`

`string`, format: `uuid`

##### `comment_id` required

- **In:** `path`

`string`, format: `uuid`

#### Request Body

##### Content-Type: application/json

- **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

**Example:**

```json
{
  "text": ""
}
```

#### Responses

##### Status: 200 OK

###### Content-Type: application/json

- **`comments` (required)**

  `array` — Comments in the thread, ordered by \`created\_at\`.

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

  - **`id` (required)**

    `string`, format: `uuid` — Unique identifier for the comment.

  - **`text` (required)**

    `string` — Plain-text comment body. Mentioning users is not supported.

  - **`user` (required)**

    `object` — Author of the comment.

    - **`id` (required)**

      `integer` — Unique identifier for the user.

    - **`name` (required)**

      `string` — Display name of the user.

    - **`profile_image_url`**

      `object` — URL to the user's profile image. Null if not available.

- **`draft_id` (required)**

  `integer` — Identifier of the draft this comment thread belongs to.

- **`id` (required)**

  `string`, format: `uuid` — Unique identifier for the comment thread.

- **`platform` (required)**

  `string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"` — Platform the comment thread is anchored on.

- **`status` (required)**

  `string`, possible values: `"unresolved", "resolved"` — Resolution status of the comment thread.

- **`selected_text`**

  `object` — The text the comment thread was originally anchored to. Captured when the thread was created and not re-derived afterwards, so it may not match the current post text if the post has been edited. Null for threads that were created against a whole post rather than a specific span.

**Example:**

```json
{
  "id": "",
  "draft_id": 1,
  "platform": "x",
  "status": "unresolved",
  "selected_text": "",
  "comments": [
    {
      "id": "",
      "text": "",
      "created_at": "",
      "user": null
    }
  ]
}
```

##### Status: 400 Request failed validation

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 422 Schema validation error

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Delete a comment

- **Method:** `DELETE`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}/comment-threads/{comment_thread_id}/comments/{comment_id}`
- **Tags:** Comments

Deletes a comment from a comment thread. If `comment_id` identifies the *root* (oldest) comment, the entire thread is deleted and the corresponding comment markers are removed from the text.

**Required permission:** Authorship of the comment or WRITE access on the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `comment_thread_id` required

- **In:** `path`

`string`, format: `uuid`

##### `comment_id` required

- **In:** `path`

`string`, format: `uuid`

#### Responses

##### Status: 204 No Content

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### Delete a comment thread

- **Method:** `DELETE`
- **Path:** `/v2/social-sets/{social_set_id}/drafts/{draft_id}/comment-threads/{comment_thread_id}`
- **Tags:** Comments

Deletes the comment thread along with all its comments and removes the corresponding comment markers from the text.

**Required permission:** Authorship of the comment thread or WRITE access on the social set.

#### Parameters

##### `social_set_id` required

- **In:** `path`

`integer`

##### `draft_id` required

- **In:** `path`

`integer`

##### `comment_thread_id` required

- **In:** `path`

`string`, format: `uuid`

#### Responses

##### Status: 204 No Content

##### Status: 401 Missing or invalid authentication

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 403 Insufficient permissions

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 404 Resource not found

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

##### Status: 429 Rate limited

###### Content-Type: application/json

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

## Webhooks

### Draft Created

- **Method:**`POST`
- **Path:**`/webhooks/draft.created`
- **Tags:** Webhooks

Triggered when a new draft is created in a social set you have access to.

### Draft Planned

- **Method:**`POST`
- **Path:**`/webhooks/draft.planned`
- **Tags:** Webhooks

Triggered when a draft transitions into the planned status: dated on the queue and calendar but inert until confirmed into a real schedule.

### Draft Scheduled

- **Method:**`POST`
- **Path:**`/webhooks/draft.scheduled`
- **Tags:** Webhooks

Triggered when a draft is scheduled for publishing.

### Draft Published

- **Method:**`POST`
- **Path:**`/webhooks/draft.published`
- **Tags:** Webhooks

Triggered when a draft is successfully published to one or more platforms.

### Draft Status Changed

- **Method:**`POST`
- **Path:**`/webhooks/draft.status_changed`
- **Tags:** Webhooks

Triggered on any status transition. This event fires alongside specific events like draft.planned, draft.scheduled, or draft.published.

### Draft Tags Changed

- **Method:**`POST`
- **Path:**`/webhooks/draft.tags_changed`
- **Tags:** Webhooks

Triggered when tags are added to or removed from a draft.

### Draft Deleted

- **Method:**`POST`
- **Path:**`/webhooks/draft.deleted`
- **Tags:** Webhooks

Triggered when a draft is deleted. The payload contains the draft data as it was before deletion.

## Schemas

### UserResponse

- **Type:**`object`

User details response schema

- **`email` (required)**

  `string` — Email address of the user

- **`id` (required)**

  `integer` — Unique identifier for the user

- **`name` (required)**

  `string` — Name of the user

- **`signup_date` (required)**

  `string`, format: `date-time` — Timestamp when the user signed up (ISO 8601 format in UTC)

- **`api_key_label`**

  `object` — Label of the API key used to authenticate this request. Null if no label was set.

- **`profile_image_url`**

  `object` — URL to the user's profile image. Null if not available.

**Example:**

```json
{
  "id": 12345,
  "name": "John Doe",
  "email": "user@example.com",
  "profile_image_url": "https://example.com/avatar.jpg",
  "signup_date": "2024-01-15T10:30:00Z",
  "api_key_label": "My Production Key"
}
```

### APIErrorResponse

- **Type:**`object`

Standard error response for all API errors.

- **`error` (required)**

  `object` — Standard error response object.

  - **`code` (required)**

    `string` — Error code

  - **`message` (required)**

    `string` — Human-readable error message

  - **`details`**

    `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "error": {
    "code": "",
    "message": "",
    "details": []
  }
}
```

### ErrorDetail

- **Type:**`object`

Field-level error detail.

- **`message` (required)**

  `string` — Human-readable error message

- **`ctx`**

  `object` — Error context

- **`field`**

  `object` — Field name that caused the error

- **`input`**

  `object` — Input value that caused the error (e.g. 'Hello, world!')

- **`loc`**

  `object` — Error location path (e.g. \['body', 'platforms', 'x', 'posts', 0, 'text'])

- **`type`**

  `object` — Error type (e.g. 'string\_too\_long', 'list\_max\_items')

**Example:**

```json
{
  "message": "",
  "field": "",
  "type": "",
  "loc": [],
  "ctx": {},
  "input": null
}
```

### ErrorObject

- **Type:**`object`

Standard error response object.

- **`code` (required)**

  `string` — Error code

- **`message` (required)**

  `string` — Human-readable error message

- **`details`**

  `object` — List of field-level errors (optional - only present when applicable)

**Example:**

```json
{
  "code": "",
  "message": "",
  "details": [
    {
      "message": "",
      "field": "",
      "type": "",
      "loc": [],
      "ctx": {},
      "input": null
    }
  ]
}
```

### Input

- **Type:**`object`

Pagination input parameters.

- **`limit`**

  `object`, default: `10` — Maximum number of items to return per page

- **`offset`**

  `integer`, default: `0` — Number of items to skip from the beginning

**Example:**

```json
{
  "limit": 10,
  "offset": 0
}
```

### AnalyticsEngagementMetrics

- **Type:**`object`

Platform-agnostic engagement metrics for a post.

- **`comments` (required)**

  `integer` — Comment/reply count for the post.

- **`likes` (required)**

  `integer` — Reaction count for the post.

- **`profile_clicks` (required)**

  `integer` — Profile click count.

- **`quotes` (required)**

  `integer` — Quote/repost-with-comment count.

- **`shares` (required)**

  `integer` — Reshare/repost count for the post.

- **`total` (required)**

  `integer` — Total engagement count across supported interaction metrics.

- **`link_clicks`**

  `object` — Outbound URL click count when available.

- **`saves`**

  `object` — Save/bookmark count when available.

**Example:**

```json
{
  "total": 1,
  "likes": 1,
  "comments": 1,
  "shares": 1,
  "quotes": 1,
  "saves": 1,
  "profile_clicks": 1,
  "link_clicks": 1
}
```

### AnalyticsPostMetrics

- **Type:**`object`

High-level metrics for a post.

- **`engagement` (required)**

  `object` — Normalized engagement metrics for this post.

  - **`comments` (required)**

    `integer` — Comment/reply count for the post.

  - **`likes` (required)**

    `integer` — Reaction count for the post.

  - **`profile_clicks` (required)**

    `integer` — Profile click count.

  - **`quotes` (required)**

    `integer` — Quote/repost-with-comment count.

  - **`shares` (required)**

    `integer` — Reshare/repost count for the post.

  - **`total` (required)**

    `integer` — Total engagement count across supported interaction metrics.

  - **`link_clicks`**

    `object` — Outbound URL click count when available.

  - **`saves`**

    `object` — Save/bookmark count when available.

- **`impressions` (required)**

  `integer` — Number of impressions for the post.

**Example:**

```json
{
  "impressions": 1,
  "engagement": {
    "total": 1,
    "likes": 1,
    "comments": 1,
    "shares": 1,
    "quotes": 1,
    "saves": 1,
    "profile_clicks": 1,
    "link_clicks": 1
  }
}
```

### PagedSocialSetAnalyticsPostResponse

- **Type:**`object`

* **`limit` (required)**

  `integer`

* **`next` (required)**

  `object`

* **`offset` (required)**

  `integer`

* **`previous` (required)**

  `object`

* **`results` (required)**

  `array`

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — When the post was created (ISO8601 in UTC).

  - **`metrics` (required)**

    `object` — Normalized post metrics grouped for external API consumers.

    - **`engagement` (required)**

      `object` — Normalized engagement metrics for this post.

      - **`comments` (required)**

        `integer` — Comment/reply count for the post.

      - **`likes` (required)**

        `integer` — Reaction count for the post.

      - **`profile_clicks` (required)**

        `integer` — Profile click count.

      - **`quotes` (required)**

        `integer` — Quote/repost-with-comment count.

      - **`shares` (required)**

        `integer` — Reshare/repost count for the post.

      - **`total` (required)**

        `integer` — Total engagement count across supported interaction metrics.

      - **`link_clicks`**

        `object` — Outbound URL click count when available.

      - **`saves`**

        `object` — Save/bookmark count when available.

    - **`impressions` (required)**

      `integer` — Number of impressions for the post.

  - **`platform` (required)**

    `string`, possible values: `"x"` — Social platform for this post. Currently only \`x\` is supported.

  - **`post_id` (required)**

    `string` — Platform post identifier serialized as a string.

  - **`preview_text` (required)**

    `string` — Post preview text content.

  - **`url` (required)**

    `string` — Public URL to the post.

  - **`draft_id`**

    `object` — Typefully draft ID that produced this post when available.

**Example:**

```json
{
  "results": [
    {
      "platform": "x",
      "post_id": "1898022122726803672",
      "draft_id": 1,
      "created_at": "",
      "preview_text": "",
      "url": "",
      "metrics": null
    }
  ],
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

### SocialSetAnalyticsPostResponse

- **Type:**`object`

Analytics payload for a social-set post.

- **`created_at` (required)**

  `string`, format: `date-time` — When the post was created (ISO8601 in UTC).

- **`metrics` (required)**

  `object` — Normalized post metrics grouped for external API consumers.

  - **`engagement` (required)**

    `object` — Normalized engagement metrics for this post.

    - **`comments` (required)**

      `integer` — Comment/reply count for the post.

    - **`likes` (required)**

      `integer` — Reaction count for the post.

    - **`profile_clicks` (required)**

      `integer` — Profile click count.

    - **`quotes` (required)**

      `integer` — Quote/repost-with-comment count.

    - **`shares` (required)**

      `integer` — Reshare/repost count for the post.

    - **`total` (required)**

      `integer` — Total engagement count across supported interaction metrics.

    - **`link_clicks`**

      `object` — Outbound URL click count when available.

    - **`saves`**

      `object` — Save/bookmark count when available.

  - **`impressions` (required)**

    `integer` — Number of impressions for the post.

- **`platform` (required)**

  `string`, possible values: `"x"` — Social platform for this post. Currently only \`x\` is supported.

- **`post_id` (required)**

  `string` — Platform post identifier serialized as a string.

- **`preview_text` (required)**

  `string` — Post preview text content.

- **`url` (required)**

  `string` — Public URL to the post.

- **`draft_id`**

  `object` — Typefully draft ID that produced this post when available.

**Example:**

```json
{
  "platform": "x",
  "post_id": "1898022122726803672",
  "draft_id": 1,
  "created_at": "",
  "preview_text": "",
  "url": "",
  "metrics": {
    "impressions": 1,
    "engagement": null
  }
}
```

### AnalyticsFollowersDataPoint

- **Type:**`object`

Daily follower count datapoint.

- **`date` (required)**

  `string`, format: `date` — Date bucket in the social set's timezone (YYYY-MM-DD).

- **`followers_count` (required)**

  `integer` — Follower count for the date bucket.

**Example:**

```json
{
  "date": "",
  "followers_count": 1
}
```

### SocialSetAnalyticsFollowersResponse

- **Type:**`object`

Followers analytics payload for a social set.

- **`data` (required)**

  `array` — Daily follower counts ordered ascending by date.

  **Items:**

  - **`date` (required)**

    `string`, format: `date` — Date bucket in the social set's timezone (YYYY-MM-DD).

  - **`followers_count` (required)**

    `integer` — Follower count for the date bucket.

- **`platform` (required)**

  `string`, possible values: `"x"` — Social platform for this analytics series. Currently only \`x\` is supported.

- **`current_followers_count`**

  `object` — Latest non-null follower count in the returned date range.

**Example:**

```json
{
  "platform": "x",
  "current_followers_count": 1,
  "data": [
    {
      "date": "",
      "followers_count": 1
    }
  ]
}
```

### PagedSocialSetListResponse

- **Type:**`object`

* **`count` (required)**

  `integer`

* **`limit` (required)**

  `integer`

* **`next` (required)**

  `object`

* **`offset` (required)**

  `integer`

* **`previous` (required)**

  `object`

* **`results` (required)**

  `array`

  **Items:**

  - **`id` (required)**

    `integer` — Unique identifier for the social set

  - **`name` (required)**

    `string` — Display name of the social media account

  - **`profile_image_url` (required)**

    `object` — URL to the profile image of the social media account. Null if not available.

  - **`username` (required)**

    `string` — Username/handle for the social media account

  - **`team`**

    `object` — Team that owns this social set. Null if the social set is owned by an individual user.

**Example:**

```json
{
  "results": [
    {
      "id": 12345,
      "username": "elonmusk",
      "name": "Elon Musk",
      "profile_image_url": "https://typefully-user-avatars.s3.amazonaws.com/_generic/account/537/twitter.jpeg",
      "team": null
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

### SocialSetListResponse

- **Type:**`object`

Details of the social set for list endpoints

- **`id` (required)**

  `integer` — Unique identifier for the social set

- **`name` (required)**

  `string` — Display name of the social media account

- **`profile_image_url` (required)**

  `object` — URL to the profile image of the social media account. Null if not available.

- **`username` (required)**

  `string` — Username/handle for the social media account

- **`team`**

  `object` — Team that owns this social set. Null if the social set is owned by an individual user.

**Example:**

```json
{
  "id": 12345,
  "username": "elonmusk",
  "name": "Elon Musk",
  "profile_image_url": "https://typefully-user-avatars.s3.amazonaws.com/_generic/account/537/twitter.jpeg",
  "team": {
    "id": "abc123def4567890",
    "name": "Marketing Team"
  }
}
```

### Team

- **Type:**`object`

Details of the team that owns this social set

- **`id` (required)**

  `string` — Unique identifier for the team

- **`name` (required)**

  `string` — Name of the team

**Example:**

```json
{
  "id": "abc123def4567890",
  "name": "Marketing Team"
}
```

### BlueskyAccount

- **Type:**`object`

Schema for Bluesky account data in API responses

- **`profile_url` (required)**

  `string` — URL to the public Bluesky profile

- **`username` (required)**

  `string` — Bluesky username/handle

- **`name`**

  `object` — Display name for the account. Null if not available.

- **`platform`**

  `string`, default: `"bluesky"` — Platform identifier

- **`profile_image_url`**

  `object` — URL to the profile image. Null if not available.

**Example:**

```json
{
  "platform": "bluesky",
  "username": "",
  "name": "",
  "profile_image_url": "",
  "profile_url": ""
}
```

### LinkedInAccount

- **Type:**`object`

Schema for LinkedIn account data in API responses

- **`name` (required)**

  `string` — Display name for the account

- **`profile_url` (required)**

  `string` — URL to the public LinkedIn profile

- **`username` (required)**

  `string` — LinkedIn username/vanity name

- **`platform`**

  `string`, default: `"linkedin"` — Platform identifier

- **`profile_image_url`**

  `object` — URL to the profile image. Null if not available.

**Example:**

```json
{
  "platform": "linkedin",
  "username": "",
  "name": "",
  "profile_image_url": "",
  "profile_url": ""
}
```

### MastodonAccount

- **Type:**`object`

Schema for Mastodon account data in API responses

- **`followers_count` (required)**

  `integer` — Number of followers on Mastodon

- **`name` (required)**

  `string` — Display name for the account

- **`profile_url` (required)**

  `string` — URL to the public Mastodon profile

- **`server` (required)**

  `string` — Server/instance for the Mastodon account

- **`username` (required)**

  `string` — Mastodon username/handle (without the server suffix)

- **`platform`**

  `string`, default: `"mastodon"` — Platform identifier

- **`profile_image_url`**

  `object` — URL to the profile image. Null if not available.

**Example:**

```json
{
  "platform": "mastodon",
  "username": "",
  "name": "",
  "profile_image_url": "",
  "profile_url": "",
  "followers_count": 1,
  "server": ""
}
```

### PlatformsDict

- **Type:**`object`

Strictly typed dictionary for platform accounts

- **`bluesky` (required)**

  `object`

- **`linkedin` (required)**

  `object`

- **`mastodon` (required)**

  `object`

- **`substack` (required)**

  `object`

- **`threads` (required)**

  `object`

- **`x` (required)**

  `object`

**Example:**

```json
{
  "x": {
    "platform": "x",
    "username": "",
    "name": "",
    "profile_image_url": "",
    "profile_url": ""
  },
  "linkedin": {
    "platform": "linkedin",
    "username": "",
    "name": "",
    "profile_image_url": "",
    "profile_url": ""
  },
  "mastodon": {
    "platform": "mastodon",
    "username": "",
    "name": "",
    "profile_image_url": "",
    "profile_url": "",
    "followers_count": 1,
    "server": ""
  },
  "threads": {
    "platform": "threads",
    "username": "",
    "name": "",
    "profile_image_url": "",
    "profile_url": ""
  },
  "bluesky": {
    "platform": "bluesky",
    "username": "",
    "name": "",
    "profile_image_url": "",
    "profile_url": ""
  },
  "substack": {
    "platform": "substack",
    "username": "",
    "name": "",
    "profile_image_url": "",
    "profile_url": ""
  }
}
```

### PublishingQuotaResponse

- **Type:**`object`

* **`remaining` (required)**

  `object` — Remaining publish slots in the active window, or "unlimited".

* **`used` (required)**

  `integer` — Number of published drafts already counted in the active window.

* **`resets_at`**

  `object` — Timestamp when the current publishing quota window resets.

**Example:**

```json
{
  "used": 1,
  "remaining": 1,
  "resets_at": ""
}
```

### SocialSetDetailResponse

- **Type:**`object`

Detailed social set response including all platform accounts

- **`id` (required)**

  `integer` — Unique identifier for the social set

- **`name` (required)**

  `string` — Display name of the social media account

- **`platforms` (required)**

  `object` — All platform accounts configured in this social set (X, LinkedIn, Mastodon, Threads, Bluesky)

  - **`bluesky` (required)**

    `object`

  - **`linkedin` (required)**

    `object`

  - **`mastodon` (required)**

    `object`

  - **`substack` (required)**

    `object`

  - **`threads` (required)**

    `object`

  - **`x` (required)**

    `object`

- **`profile_image_url` (required)**

  `object` — URL to the profile image of the social media account. Null if not available.

- **`username` (required)**

  `string` — Username/handle for the social media account

- **`publishing_quota`**

  `object` — Shared publishing quota snapshot for this social set.

- **`team`**

  `object` — Team that owns this social set. Null if the social set is owned by an individual user.

**Example:**

```json
{
  "id": 12345,
  "username": "elonmusk",
  "name": "Elon Musk",
  "profile_image_url": "https://typefully-user-avatars.s3.amazonaws.com/_generic/account/537/twitter.jpeg",
  "team": {
    "id": "abc123def4567890",
    "name": "Marketing Team"
  },
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null
  },
  "publishing_quota": {
    "used": 1,
    "remaining": 1,
    "resets_at": ""
  }
}
```

### SubstackAccount

- **Type:**`object`

Schema for Substack account data in API responses

- **`name`**

  `object` — Display name for the account. Null if not available.

- **`platform`**

  `string`, default: `"substack"` — Platform identifier

- **`profile_image_url`**

  `object` — URL to the profile image. Null if not available.

- **`profile_url`**

  `object` — URL to the public Substack profile. Null if not available.

- **`username`**

  `object` — Substack handle. Null if not available.

**Example:**

```json
{
  "platform": "substack",
  "username": "",
  "name": "",
  "profile_image_url": "",
  "profile_url": ""
}
```

### ThreadsAccount

- **Type:**`object`

Schema for Threads account data in API responses

- **`profile_url` (required)**

  `string` — URL to the public Threads profile

- **`username` (required)**

  `string` — Threads username/handle

- **`name`**

  `object` — Display name for the account. Null if not available.

- **`platform`**

  `string`, default: `"threads"` — Platform identifier

- **`profile_image_url`**

  `object` — URL to the profile image. Null if not available.

**Example:**

```json
{
  "platform": "threads",
  "username": "",
  "name": "",
  "profile_image_url": "",
  "profile_url": ""
}
```

### XAccount

- **Type:**`object`

Schema for X/Twitter account data in API responses

- **`name` (required)**

  `string` — Display name for the account

- **`profile_url` (required)**

  `string` — URL to the public X/Twitter profile

- **`username` (required)**

  `string` — X/Twitter username/handle

- **`platform`**

  `string`, default: `"x"` — Platform identifier

- **`profile_image_url`**

  `object` — URL to the profile image. Null if not available.

**Example:**

```json
{
  "platform": "x",
  "username": "",
  "name": "",
  "profile_image_url": "",
  "profile_url": ""
}
```

### DraftOrderBy

- **Type:**`string`

Allowed order\_by fields for draft listing - prevents SQL injection

**Example:**

### DraftListResponse

- **Type:**`object`

Schema for draft/thread data in API responses with standardized timestamp field names

- **`bluesky_post_enabled` (required)**

  `boolean` — Whether posting to Bluesky is enabled

- **`bluesky_post_published_at` (required)**

  `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

- **`id` (required)**

  `integer` — Unique identifier for the draft

- **`linkedin_post_enabled` (required)**

  `boolean` — Whether posting to LinkedIn is enabled

- **`linkedin_post_published_at` (required)**

  `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

- **`mastodon_post_enabled` (required)**

  `boolean` — Whether posting to Mastodon is enabled for this draft

- **`mastodon_post_published_at` (required)**

  `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

- **`private_url` (required)**

  `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

- **`published_at` (required)**

  `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published anywhere.

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this draft belongs to

- **`status` (required)**

  `string` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. Note: this reflects the stored draft lifecycle and does not flip to a 'publishing' value while an immediate publish is in flight - fetch the draft detail endpoint and read its \`publish\_state\` to track that.

- **`substack_post_enabled` (required)**

  `boolean` — Whether posting to Substack is enabled

- **`substack_post_published_at` (required)**

  `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

- **`threads_post_enabled` (required)**

  `boolean` — Whether posting to Threads is enabled

- **`threads_post_published_at` (required)**

  `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

- **`updated_at` (required)**

  `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

- **`x_post_enabled` (required)**

  `boolean` — Whether posting to X is enabled

- **`bluesky_published_url`**

  `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

- **`draft_title`**

  `object` — Internal title for the draft. Null if not set.

- **`linkedin_published_url`**

  `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

- **`mastodon_published_url`**

  `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

- **`preview`**

  `object` — Preview of the draft content (may be null for empty drafts).

- **`scheduled_date`**

  `object` — Scheduled or planned datetime in UTC (ISO 8601); for planned drafts the date is inert. Null if the draft has no date.

- **`share_url`**

  `object` — Public share URL for the draft. Null if the draft is not publicly shared.

- **`substack_published_url`**

  `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

- **`tags`**

  `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

  **Items:**

  `string`

- **`threads_published_url`**

  `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

- **`x_post_published_at`**

  `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

- **`x_published_url`**

  `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

**Example:**

```json
{
  "id": 12345,
  "preview": "Hello world",
  "scheduled_date": "2025-01-20T14:00:00Z",
  "draft_title": "My launch thread",
  "mastodon_post_enabled": true,
  "social_set_id": 67890,
  "share_url": "https://typefully.com/share/abc123",
  "private_url": "https://typefully.com/?d=12345&a=67890",
  "status": "",
  "tags": [
    "marketing",
    "product"
  ],
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-16T09:15:00Z",
  "published_at": "2025-01-20T14:00:05Z",
  "mastodon_post_published_at": "2025-01-20T14:00:05Z",
  "linkedin_post_published_at": "2025-01-20T14:00:05Z",
  "threads_post_published_at": "2025-01-20T14:00:05Z",
  "bluesky_post_published_at": "2025-01-20T14:00:05Z",
  "substack_post_published_at": "2025-01-20T14:00:05Z",
  "x_post_published_at": "2025-01-20T14:00:05Z",
  "x_post_enabled": true,
  "linkedin_post_enabled": true,
  "threads_post_enabled": true,
  "bluesky_post_enabled": true,
  "substack_post_enabled": true,
  "x_published_url": "https://x.com/username/status/1234567890",
  "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
  "mastodon_published_url": "https://mastodon.social/@username/1234567890",
  "threads_published_url": "https://www.threads.net/@username/post/ABC123",
  "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
  "substack_published_url": "https://substack.com/@username/note/c-123456789"
}
```

### PagedDraftListResponse

- **Type:**`object`

* **`count` (required)**

  `integer`

* **`limit` (required)**

  `integer`

* **`next` (required)**

  `object`

* **`offset` (required)**

  `integer`

* **`previous` (required)**

  `object`

* **`results` (required)**

  `array`

  **Items:**

  - **`bluesky_post_enabled` (required)**

    `boolean` — Whether posting to Bluesky is enabled

  - **`bluesky_post_published_at` (required)**

    `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

  - **`id` (required)**

    `integer` — Unique identifier for the draft

  - **`linkedin_post_enabled` (required)**

    `boolean` — Whether posting to LinkedIn is enabled

  - **`linkedin_post_published_at` (required)**

    `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

  - **`mastodon_post_enabled` (required)**

    `boolean` — Whether posting to Mastodon is enabled for this draft

  - **`mastodon_post_published_at` (required)**

    `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

  - **`private_url` (required)**

    `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

  - **`published_at` (required)**

    `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published anywhere.

  - **`social_set_id` (required)**

    `integer` — ID of the social set (account) this draft belongs to

  - **`status` (required)**

    `string` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. Note: this reflects the stored draft lifecycle and does not flip to a 'publishing' value while an immediate publish is in flight - fetch the draft detail endpoint and read its \`publish\_state\` to track that.

  - **`substack_post_enabled` (required)**

    `boolean` — Whether posting to Substack is enabled

  - **`substack_post_published_at` (required)**

    `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

  - **`threads_post_enabled` (required)**

    `boolean` — Whether posting to Threads is enabled

  - **`threads_post_published_at` (required)**

    `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

  - **`updated_at` (required)**

    `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

  - **`x_post_enabled` (required)**

    `boolean` — Whether posting to X is enabled

  - **`bluesky_published_url`**

    `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

  - **`draft_title`**

    `object` — Internal title for the draft. Null if not set.

  - **`linkedin_published_url`**

    `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

  - **`mastodon_published_url`**

    `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

  - **`preview`**

    `object` — Preview of the draft content (may be null for empty drafts).

  - **`scheduled_date`**

    `object` — Scheduled or planned datetime in UTC (ISO 8601); for planned drafts the date is inert. Null if the draft has no date.

  - **`share_url`**

    `object` — Public share URL for the draft. Null if the draft is not publicly shared.

  - **`substack_published_url`**

    `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

  - **`tags`**

    `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

    **Items:**

    `string`

  - **`threads_published_url`**

    `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

  - **`x_post_published_at`**

    `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

  - **`x_published_url`**

    `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

**Example:**

```json
{
  "results": [
    {
      "id": 12345,
      "preview": "Hello world",
      "scheduled_date": "2025-01-20T14:00:00Z",
      "draft_title": "My launch thread",
      "mastodon_post_enabled": true,
      "social_set_id": 67890,
      "share_url": "https://typefully.com/share/abc123",
      "private_url": "https://typefully.com/?d=12345&a=67890",
      "status": "",
      "tags": [
        "marketing",
        "product"
      ],
      "created_at": "2025-01-15T10:30:00Z",
      "updated_at": "2025-01-16T09:15:00Z",
      "published_at": "2025-01-20T14:00:05Z",
      "mastodon_post_published_at": "2025-01-20T14:00:05Z",
      "linkedin_post_published_at": "2025-01-20T14:00:05Z",
      "threads_post_published_at": "2025-01-20T14:00:05Z",
      "bluesky_post_published_at": "2025-01-20T14:00:05Z",
      "substack_post_published_at": "2025-01-20T14:00:05Z",
      "x_post_published_at": "2025-01-20T14:00:05Z",
      "x_post_enabled": true,
      "linkedin_post_enabled": true,
      "threads_post_enabled": true,
      "bluesky_post_enabled": true,
      "substack_post_enabled": true,
      "x_published_url": "https://x.com/username/status/1234567890",
      "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
      "mastodon_published_url": "https://mastodon.social/@username/1234567890",
      "threads_published_url": "https://www.threads.net/@username/post/ABC123",
      "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
      "substack_published_url": "https://substack.com/@username/note/c-123456789"
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

### BlueskySettings

- **Type:**`object`

Settings specific to Bluesky

**Example:**

```json
{}
```

### DisabledPlatform

- **Type:**`object`

Shared schema for all disabled platforms

- **`enabled` (required)**

  `boolean`, possible values: `false`

**Example:**

```json
{
  "enabled": false
}
```

### DraftDetailResponse

- **Type:**`object`

Response schema for draft creation and retrieval

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

- **`draft_id` (required)**

  `integer` — Deprecated: Use 'id' instead. Unique identifier for the draft.

- **`id` (required)**

  `integer` — Unique identifier for the draft

- **`platforms` (required)**

  `object` — Platform configurations showing which platforms are enabled and their content

  - **`bluesky`**

    `object` — Bluesky configuration

  - **`linkedin`**

    `object` — LinkedIn configuration

  - **`mastodon`**

    `object` — Mastodon configuration

  - **`substack`**

    `object` — Substack Notes configuration

  - **`threads`**

    `object` — Threads configuration

  - **`x`**

    `object` — X (Twitter) configuration

  - **`x_article`**

    `object` — X Article configuration

- **`preview` (required)**

  `string` — Text preview of the draft, smart-trimmed with a 100-character limit

- **`private_url` (required)**

  `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this draft belongs to

- **`status` (required)**

  `string`, possible values: `"draft", "scheduled", "published", "publishing", "error", "planned"` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. This reflects the stored draft lifecycle; it does not flip to 'publishing' while an immediate publish is in flight - use \`publish\_state\` to track that.

- **`bluesky_post_published_at`**

  `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

- **`bluesky_published_url`**

  `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

- **`draft_title`**

  `object` — Human-readable title for the draft. This is for internal organization only and is not posted to social media.

- **`linkedin_post_published_at`**

  `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

- **`linkedin_published_url`**

  `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

- **`mastodon_post_published_at`**

  `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

- **`mastodon_published_url`**

  `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

- **`publish_state`**

  `object` — Async publish-progress signal, separate from \`status\`. null = no publish initiated; 'in\_progress' = at least one platform is currently being posted; 'finished' = publishing has completed for all platforms. 'finished' means the job is done, not that it succeeded - read \`status\` and the per-platform published URLs (x\_published\_url, etc.) for the outcome. After publish\_at="now", poll GET /drafts/{id} until publish\_state is 'finished'.

- **`published_at`**

  `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published.

- **`scheduled_date`**

  `object` — Timestamp when the draft is scheduled to publish - or, for planned drafts, the inert planned date (ISO 8601 in UTC). Null if the draft has no date.

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped.

- **`share_url`**

  `object` — Public share URL for the draft. Null if the draft is not publicly shared.

- **`substack_post_published_at`**

  `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

- **`substack_published_url`**

  `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

- **`tags`**

  `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

  **Items:**

  `string`

- **`threads_post_published_at`**

  `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

- **`threads_published_url`**

  `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

- **`updated_at`**

  `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

- **`x_article_published_at`**

  `object` — Timestamp when the X Article was published (ISO 8601 format in UTC). Null if not published.

- **`x_article_published_url`**

  `object` — URL of the published X Article. Null if not published or URL not available.

- **`x_post_published_at`**

  `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

- **`x_published_url`**

  `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

**Example:**

```json
{
  "id": 12345,
  "social_set_id": 67890,
  "status": "draft",
  "publish_state": null,
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-16T09:15:00Z",
  "scheduled_date": "2025-01-20T14:00:00Z",
  "published_at": "2025-01-20T14:00:05Z",
  "draft_title": "Weekly Newsletter",
  "tags": [
    "marketing",
    "product"
  ],
  "preview": "Excited to announce our new feature! 🚀",
  "share_url": "https://typefully.com/share/abc123",
  "private_url": "https://typefully.com/?d=12345&a=67890",
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "x_published_url": "https://x.com/username/status/1234567890",
  "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
  "mastodon_published_url": "https://mastodon.social/@username/1234567890",
  "threads_published_url": "https://www.threads.net/@username/post/ABC123",
  "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
  "substack_published_url": "https://substack.com/@username/note/c-123456789",
  "x_article_published_url": "https://x.com/i/article/1234567890",
  "x_post_published_at": "2025-01-20T14:00:05Z",
  "linkedin_post_published_at": "2025-01-20T14:00:08Z",
  "mastodon_post_published_at": "2025-01-20T14:00:10Z",
  "threads_post_published_at": "2025-01-20T14:00:12Z",
  "bluesky_post_published_at": "2025-01-20T14:00:15Z",
  "substack_post_published_at": "2025-01-20T14:00:15Z",
  "x_article_published_at": "2025-01-20T14:00:15Z",
  "scratchpad_text": "line 1\nline 2\n\nline 4"
}
```

### EnabledBlueskyPlatformResponse

- **Type:**`object`

Enabled Bluesky platform for responses (no validation limits)

- **`enabled` (required)**

  `boolean`, possible values: `true`

- **`posts` (required)**

  `array` — List of posts for this platform

  **Items:**

  - **`text` (required)**

    `string` — The text content of the post

  - **`hide_link_preview`**

    `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

  - **`media_ids`**

    `array` — List of media IDs attached to the post.

    **Items:**

    `string`

- **`settings`**

  `object` — Bluesky-specific settings

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "",
      "media_ids": [
        ""
      ],
      "hide_link_preview": false
    }
  ],
  "settings": {}
}
```

### EnabledLinkedInPlatformResponse

- **Type:**`object`

Enabled LinkedIn platform for responses (no validation limits)

- **`enabled` (required)**

  `boolean`, possible values: `true`

- **`posts` (required)**

  `array` — List of posts for this platform

  **Items:**

  - **`text` (required)**

    `string` — The text content of the post

  - **`hide_link_preview`**

    `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

  - **`linkedin_reshare_urn`**

    `object` — LinkedIn-only: canonical URN of the LinkedIn post reshared (repost) by this post. Null when no reshare is configured.

  - **`media_ids`**

    `array` — List of media IDs attached to the post.

    **Items:**

    `string`

- **`settings`**

  `object` — LinkedIn-specific settings

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "",
      "media_ids": [
        ""
      ],
      "linkedin_reshare_urn": "",
      "hide_link_preview": false
    }
  ],
  "settings": {}
}
```

### EnabledMastodonPlatformResponse

- **Type:**`object`

Enabled Mastodon platform for responses (no validation limits)

- **`enabled` (required)**

  `boolean`, possible values: `true`

- **`posts` (required)**

  `array` — List of posts for this platform

  **Items:**

  - **`text` (required)**

    `string` — The text content of the post

  - **`hide_link_preview`**

    `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

  - **`media_ids`**

    `array` — List of media IDs attached to the post.

    **Items:**

    `string`

- **`settings`**

  `object` — Mastodon-specific settings

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "",
      "media_ids": [
        ""
      ],
      "hide_link_preview": false
    }
  ],
  "settings": {}
}
```

### EnabledSubstackPlatformResponse

- **Type:**`object`

Enabled Substack platform for responses (no validation limits)

- **`enabled` (required)**

  `boolean`, possible values: `true`

- **`posts` (required)**

  `array` — List of posts for this platform

  **Items:**

  - **`text` (required)**

    `string` — The text content of the post

  - **`hide_link_preview`**

    `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

  - **`media_ids`**

    `array` — List of media IDs attached to the post.

    **Items:**

    `string`

- **`settings`**

  `object` — Substack-specific settings

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "",
      "media_ids": [
        ""
      ],
      "hide_link_preview": false
    }
  ],
  "settings": {}
}
```

### EnabledThreadsPlatformResponse

- **Type:**`object`

Enabled Threads platform for responses (no validation limits)

- **`enabled` (required)**

  `boolean`, possible values: `true`

- **`posts` (required)**

  `array` — List of posts for this platform

  **Items:**

  - **`text` (required)**

    `string` — The text content of the post

  - **`hide_link_preview`**

    `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

  - **`media_ids`**

    `array` — List of media IDs attached to the post.

    **Items:**

    `string`

- **`settings`**

  `object` — Threads-specific settings

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "",
      "media_ids": [
        ""
      ],
      "hide_link_preview": false
    }
  ],
  "settings": {}
}
```

### EnabledXPlatformResponse

- **Type:**`object`

Enabled X platform for responses (no validation limits)

- **`enabled` (required)**

  `boolean`, possible values: `true`

- **`posts` (required)**

  `array` — List of posts for this platform

  **Items:**

  - **`text` (required)**

    `string` — The text content of the post

  - **`hide_link_preview`**

    `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

  - **`made_with_ai`**

    `boolean`, default: `false` — X-only: Whether this post is labeled as made with AI.

  - **`media_ids`**

    `array` — List of media IDs attached to the post.

    **Items:**

    `string`

  - **`paid_partnership`**

    `boolean`, default: `false` — X-only: Whether this post is labeled as a paid partnership.

  - **`quote_post_url`**

    `object` — X-only: URL of the X post quoted by this post. Null when no quoted post is configured.

  - **`reply_settings`**

    `object` — X-only: Who can reply to this post. Null means X's default (everyone can reply).

  - **`subscribers_only`**

    `boolean`, default: `false` — X-only: Whether this individual post is visible only to paying Subscribers.

- **`settings`**

  `object` — X-specific settings

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "",
      "media_ids": [
        ""
      ],
      "quote_post_url": "",
      "subscribers_only": true,
      "reply_settings": "following",
      "paid_partnership": true,
      "made_with_ai": true,
      "hide_link_preview": false
    }
  ],
  "settings": {
    "reply_to_url": "https://x.com/therajatkapoor/status/1399394576951959554",
    "community_id": "",
    "share_with_followers": true
  }
}
```

### LinkPreviewPostResponse

- **Type:**`object`

Schema for post content with link-preview controls in API responses.

- **`text` (required)**

  `string` — The text content of the post

- **`hide_link_preview`**

  `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

- **`media_ids`**

  `array` — List of media IDs attached to the post.

  **Items:**

  `string`

**Example:**

```json
{
  "text": "",
  "media_ids": [
    ""
  ],
  "hide_link_preview": false
}
```

### LinkedInPostResponse

- **Type:**`object`

Schema for LinkedIn post content in API responses.

- **`text` (required)**

  `string` — The text content of the post

- **`hide_link_preview`**

  `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

- **`linkedin_reshare_urn`**

  `object` — LinkedIn-only: canonical URN of the LinkedIn post reshared (repost) by this post. Null when no reshare is configured.

- **`media_ids`**

  `array` — List of media IDs attached to the post.

  **Items:**

  `string`

**Example:**

```json
{
  "text": "",
  "media_ids": [
    ""
  ],
  "linkedin_reshare_urn": "",
  "hide_link_preview": false
}
```

### LinkedInSettings

- **Type:**`object`

Settings specific to LinkedIn

**Example:**

```json
{}
```

### MastodonSettings

- **Type:**`object`

Settings specific to Mastodon

**Example:**

```json
{}
```

### PlatformsResponse

- **Type:**`object`

Schema for platform configurations in responses (no validation limits)

- **`bluesky`**

  `object` — Bluesky configuration

- **`linkedin`**

  `object` — LinkedIn configuration

- **`mastodon`**

  `object` — Mastodon configuration

- **`substack`**

  `object` — Substack Notes configuration

- **`threads`**

  `object` — Threads configuration

- **`x`**

  `object` — X (Twitter) configuration

- **`x_article`**

  `object` — X Article configuration

**Example:**

```json
{
  "x": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "linkedin": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "mastodon": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "threads": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "bluesky": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "substack": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "x_article": {
    "content_markdown": "",
    "cover_media_id": ""
  }
}
```

### SubstackSettings

- **Type:**`object`

Settings specific to Substack

**Example:**

```json
{}
```

### ThreadsSettings

- **Type:**`object`

Settings specific to Threads

**Example:**

```json
{}
```

### XArticlePlatformResponse

- **Type:**`object`

X Article platform data in responses.

- **`content_markdown` (required)**

  `string` — X Article Markdown. The first non-empty block must be \`# Title\`; remaining blocks are body. Supports paragraphs, blockquotes, lists, \`#\`/\`##\` headings, bold, italic, strikethrough, links, fenced code with an optional single-token language, and standalone \`---\`/\`\*\*\*\` dividers (\`GET\` returns \`---\`). Code interiors stay literal. Escape delimiter-only prose such as \`\\---\`. Embeds must be alone on a line: \`\<typ:media media\_id="..." />\`, \`\<typ:x-post url="https\://x.com/user/status/123" />\`. Preserve comment-thread marker tags from \`GET\` when PATCHing. \`GET\` returns canonical Markdown, not submitted bytes.

- **`cover_media_id`**

  `object` — Media ID for the X Article cover image, or null when no cover is configured.

**Example:**

```json
{
  "content_markdown": "",
  "cover_media_id": ""
}
```

### XPostResponse

- **Type:**`object`

Schema for X post content in API responses.

- **`text` (required)**

  `string` — The text content of the post

- **`hide_link_preview`**

  `boolean`, default: `false` — Whether the link-preview card is suppressed for this post.

- **`made_with_ai`**

  `boolean`, default: `false` — X-only: Whether this post is labeled as made with AI.

- **`media_ids`**

  `array` — List of media IDs attached to the post.

  **Items:**

  `string`

- **`paid_partnership`**

  `boolean`, default: `false` — X-only: Whether this post is labeled as a paid partnership.

- **`quote_post_url`**

  `object` — X-only: URL of the X post quoted by this post. Null when no quoted post is configured.

- **`reply_settings`**

  `object` — X-only: Who can reply to this post. Null means X's default (everyone can reply).

- **`subscribers_only`**

  `boolean`, default: `false` — X-only: Whether this individual post is visible only to paying Subscribers.

**Example:**

```json
{
  "text": "",
  "media_ids": [
    ""
  ],
  "quote_post_url": "",
  "subscribers_only": true,
  "reply_settings": "following",
  "paid_partnership": true,
  "made_with_ai": true,
  "hide_link_preview": false
}
```

### XSettings

- **Type:**`object`

Settings specific to X (Twitter)

- **`community_id`**

  `object` — ID of the X community to post to. Find the ID in the community URL (e.g., x.com/i/communities/1493446837214187523). You must have permission to post to the community, otherwise publishing will fail.

- **`reply_to_url`**

  `object` — URL of the X post to reply to. When provided, the first post in your thread will be posted as a reply.

- **`share_with_followers`**

  `object` — When posting to a community, whether to also share the post to your timeline/followers. Defaults to true if not specified. Only has an effect when community\_id is provided.

**Example:**

```json
{
  "reply_to_url": "https://x.com/therajatkapoor/status/1399394576951959554",
  "community_id": "",
  "share_with_followers": true
}
```

### BlueskyPlatform

- **Type:**`object`

Configuration for Bluesky

**All of:**

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "Hello world! This is my first post.",
      "media_ids": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "hide_link_preview": true
    }
  ],
  "settings": {}
}
```

### DraftCreateRequest

- **Type:**`object`

Request schema for creating a draft

- **`platforms` (required)**

  `object` — Platform configurations for each social media platform

  - **`bluesky`**

    `object` — Bluesky configuration

  - **`linkedin`**

    `object` — LinkedIn configuration

  - **`mastodon`**

    `object` — Mastodon configuration

  - **`substack`**

    `object` — Substack Notes configuration

  - **`threads`**

    `object` — Threads configuration

  - **`x`**

    `object` — X (Twitter) configuration

  - **`x_article`**

    `object` — X Article configuration. This platform is standalone: do not combine it with other platforms.

- **`draft_title`**

  `object` — Draft title, for internal organization only; not posted to social media.

- **`plan_at`**

  `object` — When to plan the draft. A planned draft is dated but inert: it shows on the queue and calendar at its date but never auto-publishes until confirmed by later setting \`publish\_at\`. Accepts "next-free-slot" or a future ISO 8601 datetime with timezone ("now" is not valid). Mutually exclusive with \`publish\_at\`. Omit to save as a plain draft.

- **`publish_at`**

  `object` — When to publish: "now" (immediate), "next-free-slot" (next available posting slot), or a future ISO 8601 datetime with timezone. Omit to save as a draft. Mutually exclusive with \`plan\_at\`. "now" is asynchronous: the response returns \`publish\_state\`="in\_progress" while \`status\` stays "draft" and published URLs are null - success, not failure; poll GET /drafts/{id} until \`publish\_state\`="finished", then read \`status\` and the published URLs.

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped.

- **`share`**

  `boolean`, default: `false` — Whether to generate a public share URL for this draft. When true, anyone with the URL can view the draft content.

- **`tags`**

  `array` — Tag slugs (not names). Tags must already exist in the social set - list them via /tags.

  **Items:**

  `string`

**Example:**

```json
{
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "draft_title": "Weekly Newsletter",
  "scratchpad_text": "line 1\nline 2\n\nline 4",
  "tags": [
    "marketing",
    "product"
  ],
  "share": false,
  "publish_at": "2027-12-20T09:00:00-05:00",
  "plan_at": "2027-12-20T09:00:00-05:00"
}
```

### LinkPreviewPost

- **Type:**`object`

Schema for individual post content on platforms with link-preview controls.

- **`text` (required)**

  `string` — The text content of the post.

- **`hide_link_preview`**

  `object` — Hide the link-preview card (the URL stays as plain text). When false, a preview is attached for the last URL. Omit (or send null) to keep the current value on update (false on create).

- **`media_ids`**

  `array` — Media IDs to attach to the post; obtain them via the media upload endpoint.

  **Items:**

  `string`

**Example:**

```json
{
  "text": "Hello world! This is my first post.",
  "media_ids": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "hide_link_preview": true
}
```

### LinkedInPlatform

- **Type:**`object`

Configuration for LinkedIn

**All of:**

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "Thanks @[Typefully](urn:li:organization:86779668) for the support!",
      "media_ids": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "hide_link_preview": true,
      "linkedin_reshare_target": "urn:li:share:7437089188157554688"
    }
  ],
  "settings": {}
}
```

### LinkedInPost

- **Type:**`object`

Schema for individual LinkedIn post content.

- **`text` (required)**

  `string` — The text content of the post. You can tag companies with mention syntax: @\[Company Name]\(urn:li:organization:123456).

- **`hide_link_preview`**

  `object` — Hide the link-preview card (the URL stays as plain text). When false, a preview is attached for the last URL. Omit (or send null) to keep the current value on update (false on create).

- **`linkedin_reshare_target`**

  `object` — Canonical URN or full LinkedIn post URL to reshare (repost). URLs are normalized to the canonical URN in storage and responses. Use values like urn:li:share:\<id>, urn:li:ugcPost:\<id>, or urn:li:groupPost:\<id>.

- **`media_ids`**

  `array` — Media IDs to attach to the post; obtain them via the media upload endpoint.

  **Items:**

  `string`

**Example:**

```json
{
  "text": "Thanks @[Typefully](urn:li:organization:86779668) for the support!",
  "media_ids": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "hide_link_preview": true,
  "linkedin_reshare_target": "urn:li:share:7437089188157554688"
}
```

### MastodonPlatform

- **Type:**`object`

Configuration for Mastodon

**All of:**

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "Hello world! This is my first post.",
      "media_ids": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "hide_link_preview": true
    }
  ],
  "settings": {}
}
```

### Platforms

- **Type:**`object`

Schema for all platform configurations

- **`bluesky`**

  `object` — Bluesky configuration

- **`linkedin`**

  `object` — LinkedIn configuration

- **`mastodon`**

  `object` — Mastodon configuration

- **`substack`**

  `object` — Substack Notes configuration

- **`threads`**

  `object` — Threads configuration

- **`x`**

  `object` — X (Twitter) configuration

- **`x_article`**

  `object` — X Article configuration. This platform is standalone: do not combine it with other platforms.

**Example:**

````json
{
  "x": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "linkedin": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "mastodon": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "threads": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "bluesky": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "substack": {
    "enabled": true,
    "posts": [],
    "settings": null
  },
  "x_article": {
    "content_markdown": "# Think Different, Draft Different\n\nGreat drafts start when builders **question defaults**, *shape the rough edges*, and ~~wait for perfect certainty~~ publish what helps.\n\n> The best interface is the one readers forget they are using.\n\n# Working notes\n\n- Start with a sharp title\n- Use structure before decoration\n- Link only when [context helps](https://typefully.com)\n\n## Final pass\n\n1. Cut filler\n2. Keep the useful tension\n3. Ship the clearer version\n\n```python\nprint(\"Ship it\")\n```",
    "cover_media_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}
````

### SubstackPlatform

- **Type:**`object`

Configuration for Substack Notes. Substack doesn't support threads, so `posts` must contain a single post.

**All of:**

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "Hello world! This is my first post.",
      "media_ids": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "hide_link_preview": true
    }
  ],
  "settings": {}
}
```

### ThreadsPlatform

- **Type:**`object`

Configuration for Threads

**All of:**

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "Hello world! This is my first post.",
      "media_ids": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "hide_link_preview": true
    }
  ],
  "settings": {}
}
```

### XArticlePlatform

- **Type:**`object`

Configuration for an X Article draft.

- **`content_markdown`**

  `object` — X Article Markdown. The first non-empty block must be \`# Title\`; remaining blocks are body. Supports paragraphs, blockquotes, lists, \`#\`/\`##\` headings, bold, italic, strikethrough, links, fenced code with an optional single-token language, and standalone \`---\`/\`\*\*\*\` dividers (\`GET\` returns \`---\`). Code interiors stay literal. Escape delimiter-only prose such as \`\\---\`. Embeds must be alone on a line: \`\<typ:media media\_id="..." />\`, \`\<typ:x-post url="https\://x.com/user/status/123" />\`. Preserve comment-thread marker tags from \`GET\` when PATCHing. \`GET\` returns canonical Markdown, not submitted bytes. Required when creating an X Article draft; optional on PATCH when only changing \`cover\_media\_id\`.

- **`cover_media_id`**

  `object` — Optional media ID for the X Article cover image. Must reference an uploaded, ready, account-owned static image. Omit on PATCH to keep the current cover; send null to remove it.

**Example:**

````json
{
  "content_markdown": "# Think Different, Draft Different\n\nGreat drafts start when builders **question defaults**, *shape the rough edges*, and ~~wait for perfect certainty~~ publish what helps.\n\n> The best interface is the one readers forget they are using.\n\n# Working notes\n\n- Start with a sharp title\n- Use structure before decoration\n- Link only when [context helps](https://typefully.com)\n\n## Final pass\n\n1. Cut filler\n2. Keep the useful tension\n3. Ship the clearer version\n\n```python\nprint(\"Ship it\")\n```",
  "cover_media_id": "550e8400-e29b-41d4-a716-446655440000"
}
````

### XPlatform

- **Type:**`object`

Configuration for X (Twitter)

**All of:**

**Example:**

```json
{
  "enabled": true,
  "posts": [
    {
      "text": "Hello world! This is my first post.",
      "media_ids": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "hide_link_preview": true,
      "quote_post_url": "https://x.com/typefully/status/2025894220243063023",
      "subscribers_only": false,
      "reply_settings": "following",
      "paid_partnership": false,
      "made_with_ai": false
    }
  ],
  "settings": {
    "reply_to_url": "https://x.com/therajatkapoor/status/1399394576951959554",
    "community_id": "",
    "share_with_followers": true
  }
}
```

### XPost

- **Type:**`object`

Schema for individual X post content.

- **`text` (required)**

  `string` — The text content of the post.

- **`hide_link_preview`**

  `object` — Hide the link-preview card (the URL stays as plain text). When false, a preview is attached for the last URL. Omit (or send null) to keep the current value on update (false on create).

- **`made_with_ai`**

  `boolean`, default: `false` — Whether this post should be labeled as made with AI.

- **`media_ids`**

  `array` — Media IDs to attach to the post; obtain them via the media upload endpoint.

  **Items:**

  `string`

- **`paid_partnership`**

  `boolean`, default: `false` — Whether this post should be labeled as a paid partnership.

- **`quote_post_url`**

  `object` — URL of the X post to quote in this post (equivalent to Typefully's 'convert to quote' action).

- **`reply_settings`**

  `object` — Who can reply. Null (default) means everyone.

- **`subscribers_only`**

  `boolean`, default: `false` — Visible only to paying Subscribers. Requires an X account approved for creator Subscriptions.

**Example:**

```json
{
  "text": "Hello world! This is my first post.",
  "media_ids": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "hide_link_preview": true,
  "quote_post_url": "https://x.com/typefully/status/2025894220243063023",
  "subscribers_only": false,
  "reply_settings": "following",
  "paid_partnership": false,
  "made_with_ai": false
}
```

### DraftUpdateRequest

- **Type:**`object`

Request schema for updating a draft (partial updates supported)

- **`draft_title`**

  `object` — Draft title, for internal organization only; not posted to social media. Omit to keep unchanged.

- **`force_overwrite_comments`**

  `boolean`, default: `false` — Comment-thread anchor preservation toggle. When false (the default), submitting \`posts\[\*].text\` or X Article \`content\_markdown\` whose \`\<typ:comment-thread>\` markers don't match the draft's stored comment threads is rejected with \`409 COMMENTS\_MARKER\_MISMATCH\`; re-include the missing markers and retry. When true, missing markers are accepted: their comment threads are resolved server-side and their anchors stripped; submitted markers still validate normally. Only JSON \`true\`/\`false\` (not \`"true"\` strings).

- **`plan_at`**

  `object` — When to plan. A planned draft is dated but inert: on the queue and calendar but never auto-publishing until confirmed via \`publish\_at\`. A future datetime with timezone or "next-free-slot" plans a plain draft, replans a planned one, or unschedules a scheduled one into a plan (requires publish access; "now" invalid). Explicit null returns a planned or scheduled draft to plain draft status. Mutually exclusive with \`publish\_at\`. Omit to keep unchanged.

- **`platforms`**

  `object` — Platform configurations. Only provided platforms will be updated; omitted platforms remain unchanged.

- **`publish_at`**

  `object` — When to publish: "now" (immediate), "next-free-slot", or a future ISO 8601 datetime with timezone. On a planned draft, a datetime or "next-free-slot" confirms it into a real schedule (echo its \`scheduled\_date\` to keep the date); "now" publishes it immediately. Explicit null returns it to plain draft status. Mutually exclusive with \`plan\_at\`. Omit to keep unchanged. "now" is asynchronous: the response returns \`publish\_state\`="in\_progress" (success, not failure); poll GET /drafts/{id} until "finished".

- **`scratchpad_text`**

  `object` — Plain text scratchpad notes for the draft. Formatting is stripped. Omit to keep unchanged.

- **`share`**

  `object` — Whether to generate a public share URL. Omit to keep unchanged.

- **`tags`**

  `object` — Tag slugs (not names). Tags must already exist in the social set - list them via /tags. Omit to keep unchanged.

**Example:**

```json
{
  "platforms": {
    "x": null,
    "linkedin": null,
    "mastodon": null,
    "threads": null,
    "bluesky": null,
    "substack": null,
    "x_article": null
  },
  "draft_title": "Weekly Newsletter",
  "scratchpad_text": "line 1\nline 2\n\nline 4",
  "tags": [
    "marketing",
    "product"
  ],
  "share": true,
  "publish_at": "2027-12-20T09:00:00-05:00",
  "plan_at": "2027-12-20T09:00:00-05:00",
  "force_overwrite_comments": false
}
```

### MediaUploadResponse

- **Type:**`object`

Schema for media upload response

- **`media_id` (required)**

  `string`, format: `uuid` — Unique identifier for the uploaded media. Use this ID when attaching media to posts.

- **`upload_url` (required)**

  `string` — Presigned S3 URL for uploading the file. PUT the file content to this URL within 1 hour.

**Example:**

```json
{
  "media_id": "550e8400-e29b-41d4-a716-446655440000",
  "upload_url": "https://s3.amazonaws.com/bucket/path/file?signature=..."
}
```

### MediaUploadRequest

- **Type:**`object`

Schema for media upload request

- **`file_name` (required)**

  `string` — Original filename with extension (e.g., 'image.jpg', 'video.mp4'). Used for MIME type detection and display. Allowed characters: letters, numbers, hyphens, underscores, periods, parentheses. Allowed extensions: .jpg, .jpeg, .png, .webp, .gif, .mp4, .mov, .pdf

- **`alt_text`**

  `object` — Accessibility description for the media, used as alt text when publishing. Can also be set later with PATCH /media/{media\_id}. Platform-specific character limits are validated when the media is attached to a draft.

**Example:**

```json
{
  "file_name": "profile-photo.jpg",
  "alt_text": "A golden retriever catching a frisbee mid-air in a sunny park"
}
```

### MediaStatusResponse

- **Type:**`object`

Schema for media status response

- **`file_name` (required)**

  `string` — Original filename

- **`media_id` (required)**

  `string`, format: `uuid` — Unique identifier for the media file

- **`status` (required)**

  `string`, possible values: `"processing", "ready", "failed"` — Processing status: 'processing' = file is being processed, 'ready' = file is ready to use in posts, 'failed' = processing failed

- **`alt_text`**

  `object` — Accessibility description for the media, used as alt text when publishing. Omitted when not set.

- **`error_reason`**

  `object` — Error message if status is 'failed'. Null otherwise.

- **`media_urls`**

  `object` — Dictionary of media URLs. For resizable images (PNG, JPG, WEBP): includes 'small' (600px), 'medium' (1200px), 'large' (3200px), and 'original'. For non-resizable media (GIF, video, PDF): includes only 'original'.

- **`mime`**

  `object` — MIME type of the media file

**Example:**

```json
{
  "media_id": "",
  "file_name": "photo.jpg",
  "mime": "image/jpeg",
  "status": "ready",
  "error_reason": "Unsupported file format",
  "alt_text": "A golden retriever catching a frisbee mid-air in a sunny park",
  "media_urls": {
    "large": "https://cdn.../resized/...",
    "medium": "https://cdn.../resized/...",
    "original": "https://cdn.../original/...",
    "small": "https://cdn.../resized/..."
  }
}
```

### MediaUpdateRequest

- **Type:**`object`

Schema for media update request

- **`alt_text`**

  `object` — Accessibility description for the media, used as alt text when publishing. Pass null to clear it. If omitted, the current value is kept.

**Example:**

```json
{
  "alt_text": "A golden retriever catching a frisbee mid-air in a sunny park"
}
```

### PagedTagResponse

- **Type:**`object`

* **`count` (required)**

  `integer`

* **`limit` (required)**

  `integer`

* **`next` (required)**

  `object`

* **`offset` (required)**

  `integer`

* **`previous` (required)**

  `object`

* **`results` (required)**

  `array`

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the tag was created (ISO 8601 format in UTC)

  - **`name` (required)**

    `string` — Display name for the tag

  - **`slug` (required)**

    `string` — Auto-generated URL-safe identifier from the tag name

**Example:**

```json
{
  "results": [
    {
      "slug": "marketing",
      "name": "Marketing",
      "created_at": "2025-01-15T10:30:00Z"
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

### TagResponse

- **Type:**`object`

Response schema for tag data

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the tag was created (ISO 8601 format in UTC)

- **`name` (required)**

  `string` — Display name for the tag

- **`slug` (required)**

  `string` — Auto-generated URL-safe identifier from the tag name

**Example:**

```json
{
  "slug": "marketing",
  "name": "Marketing",
  "created_at": "2025-01-15T10:30:00Z"
}
```

### TagCreateRequest

- **Type:**`object`

Request schema for creating a tag

- **`name` (required)**

  `string` — Display name for the tag. The slug will be auto-generated from this name.

**Example:**

```json
{
  "name": "Marketing"
}
```

### QueueScheduleResponse

- **Type:**`object`

Response schema for queue schedule rules.

- **`rules` (required)**

  `array` — Schedule rules in local time.

  **Items:**

  - **`days` (required)**

    `array` — Days of week this rule applies to

    **Items:**

    `string`, possible values: `"mon", "tue", "wed", "thu", "fri", "sat", "sun"`

  - **`h` (required)**

    `integer` — Hour in 24h clock (0-23)

  - **`m` (required)**

    `integer` — Minute (0-59)

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this schedule belongs to.

- **`timezone` (required)**

  `string` — Timezone name for this social set.

**Example:**

```json
{
  "social_set_id": 123,
  "timezone": "America/New_York",
  "rules": [
    {
      "h": 0,
      "m": 0,
      "days": [
        "mon",
        "tue",
        "wed",
        "thu",
        "fri"
      ]
    }
  ]
}
```

### QueueScheduleRuleSchema

- **Type:**`object`

Single schedule rule (local time + days of week).

- **`days` (required)**

  `array` — Days of week this rule applies to

  **Items:**

  `string`, possible values: `"mon", "tue", "wed", "thu", "fri", "sat", "sun"`

- **`h` (required)**

  `integer` — Hour in 24h clock (0-23)

- **`m` (required)**

  `integer` — Minute (0-59)

**Example:**

```json
{
  "h": 0,
  "m": 0,
  "days": [
    "mon",
    "tue",
    "wed",
    "thu",
    "fri"
  ]
}
```

### QueueScheduleUpdateRequest

- **Type:**`object`

Request schema for fully replacing queue schedule rules.

- **`rules` (required)**

  `array` — New schedule rules (full replacement).

  **Items:**

  - **`days` (required)**

    `array` — Days of week this rule applies to

    **Items:**

    `string`, possible values: `"mon", "tue", "wed", "thu", "fri", "sat", "sun"`

  - **`h` (required)**

    `integer` — Hour in 24h clock (0-23)

  - **`m` (required)**

    `integer` — Minute (0-59)

**Example:**

```json
{
  "rules": [
    {
      "h": 0,
      "m": 0,
      "days": [
        "mon",
        "tue",
        "wed",
        "thu",
        "fri"
      ]
    }
  ]
}
```

### QueueDaySchema

- **Type:**`object`

Queue day with its queue items.

- **`date` (required)**

  `string` — YYYY-MM-DD in the social set timezone

- **`items` (required)**

  `array` — Queue items for this day (in chronological order).

  **Items:**

  - **`at` (required)**

    `string`, format: `date-time` — ISO8601 datetime in UTC.

  - **`kind` (required)**

    `string`, possible values: `"queue_slot", "custom_time"` — Queue item kind. - \`queue\_slot\`: schedule-generated queue slot time (free slot if \`draft\` is null) - \`custom\_time\`: scheduled or planned draft that does not count as a slot occupant (custom-time scheduling, or collision overflow)

  - **`draft`**

    `object` — Scheduled or planned draft occupying this time (check \`status\` to tell them apart). Null means the slot is free.

**Example:**

```json
{
  "date": "",
  "items": [
    {
      "at": "2026-02-12T22:00:00Z",
      "kind": "queue_slot",
      "draft": null
    }
  ]
}
```

### QueueItemSchema

- **Type:**`object`

Queue items are time points in the queue.

- `at` is an ISO8601 datetime in UTC.
- `kind` differentiates schedule-generated queue slots from scheduled/planned drafts that do not occupy a slot.
- `draft` is either null (free slot) or a DraftListResponse object.

* **`at` (required)**

  `string`, format: `date-time` — ISO8601 datetime in UTC.

* **`kind` (required)**

  `string`, possible values: `"queue_slot", "custom_time"` — Queue item kind. - \`queue\_slot\`: schedule-generated queue slot time (free slot if \`draft\` is null) - \`custom\_time\`: scheduled or planned draft that does not count as a slot occupant (custom-time scheduling, or collision overflow)

* **`draft`**

  `object` — Scheduled or planned draft occupying this time (check \`status\` to tell them apart). Null means the slot is free.

**Example:**

```json
{
  "at": "2026-02-12T22:00:00Z",
  "kind": "queue_slot",
  "draft": {
    "id": 12345,
    "preview": "Hello world",
    "scheduled_date": "2025-01-20T14:00:00Z",
    "draft_title": "My launch thread",
    "mastodon_post_enabled": true,
    "social_set_id": 67890,
    "share_url": "https://typefully.com/share/abc123",
    "private_url": "https://typefully.com/?d=12345&a=67890",
    "status": "",
    "tags": [
      "marketing",
      "product"
    ],
    "created_at": "2025-01-15T10:30:00Z",
    "updated_at": "2025-01-16T09:15:00Z",
    "published_at": "2025-01-20T14:00:05Z",
    "mastodon_post_published_at": "2025-01-20T14:00:05Z",
    "linkedin_post_published_at": "2025-01-20T14:00:05Z",
    "threads_post_published_at": "2025-01-20T14:00:05Z",
    "bluesky_post_published_at": "2025-01-20T14:00:05Z",
    "substack_post_published_at": "2025-01-20T14:00:05Z",
    "x_post_published_at": "2025-01-20T14:00:05Z",
    "x_post_enabled": true,
    "linkedin_post_enabled": true,
    "threads_post_enabled": true,
    "bluesky_post_enabled": true,
    "substack_post_enabled": true,
    "x_published_url": "https://x.com/username/status/1234567890",
    "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
    "mastodon_published_url": "https://mastodon.social/@username/1234567890",
    "threads_published_url": "https://www.threads.net/@username/post/ABC123",
    "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
    "substack_published_url": "https://substack.com/@username/note/c-123456789"
  }
}
```

### QueueResponse

- **Type:**`object`

Response schema for the queue date-range view.

- **`days` (required)**

  `array` — Days in the requested date range (inclusive).

  **Items:**

  - **`date` (required)**

    `string` — YYYY-MM-DD in the social set timezone

  - **`items` (required)**

    `array` — Queue items for this day (in chronological order).

    **Items:**

    - **`at` (required)**

      `string`, format: `date-time` — ISO8601 datetime in UTC.

    - **`kind` (required)**

      `string`, possible values: `"queue_slot", "custom_time"` — Queue item kind. - \`queue\_slot\`: schedule-generated queue slot time (free slot if \`draft\` is null) - \`custom\_time\`: scheduled or planned draft that does not count as a slot occupant (custom-time scheduling, or collision overflow)

    - **`draft`**

      `object` — Scheduled or planned draft occupying this time (check \`status\` to tell them apart). Null means the slot is free.

- **`end_date` (required)**

  `string` — YYYY-MM-DD in the social set timezone

- **`social_set_id` (required)**

  `integer` — ID of the social set (account) this queue belongs to.

- **`start_date` (required)**

  `string` — YYYY-MM-DD in the social set timezone

**Example:**

```json
{
  "social_set_id": 123,
  "start_date": "2026-02-01",
  "end_date": "2026-02-29",
  "days": [
    {
      "date": "",
      "items": []
    }
  ]
}
```

### LinkedInOrganizationFromURLResponse

- **Type:**`object`

Resolved LinkedIn organization metadata.

- **`id` (required)**

  `string` — LinkedIn organization ID

- **`mention_text` (required)**

  `string` — Ready-to-use LinkedIn mention syntax to paste into post text.

- **`urn` (required)**

  `string` — LinkedIn organization URN, usable in mention syntax

- **`description`**

  `object` — Organization description

- **`logo_url`**

  `object` — Organization logo URL

- **`name`**

  `object` — Organization display name

- **`url`**

  `object` — Public LinkedIn company URL

- **`vanity_name`**

  `object` — LinkedIn vanity name

- **`website`**

  `object` — Organization website

**Example:**

```json
{
  "id": "86779668",
  "urn": "urn:li:organization:86779668",
  "mention_text": "@[Typefully](urn:li:organization:86779668)",
  "name": "",
  "vanity_name": "",
  "description": "",
  "website": "",
  "logo_url": "",
  "url": ""
}
```

### CommentResponse

- **Type:**`object`

A single comment within a comment thread.

- **`created_at` (required)**

  `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

- **`id` (required)**

  `string`, format: `uuid` — Unique identifier for the comment.

- **`text` (required)**

  `string` — Plain-text comment body. Mentioning users is not supported.

- **`user` (required)**

  `object` — Author of the comment.

  - **`id` (required)**

    `integer` — Unique identifier for the user.

  - **`name` (required)**

    `string` — Display name of the user.

  - **`profile_image_url`**

    `object` — URL to the user's profile image. Null if not available.

**Example:**

```json
{
  "id": "",
  "text": "",
  "created_at": "",
  "user": {
    "id": 1,
    "name": "",
    "profile_image_url": ""
  }
}
```

### CommentThreadListResponse

- **Type:**`object`

Paginated list of comment threads for a draft.

- **`count` (required)**

  `integer` — Total number of comment threads available.

- **`limit` (required)**

  `integer` — Items per page used for this request.

- **`offset` (required)**

  `integer` — Current offset value.

- **`results` (required)**

  `array` — Comment threads in the current page.

  **Items:**

  - **`comments` (required)**

    `array` — Comments in the thread, ordered by \`created\_at\`.

    **Items:**

    - **`created_at` (required)**

      `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

    - **`id` (required)**

      `string`, format: `uuid` — Unique identifier for the comment.

    - **`text` (required)**

      `string` — Plain-text comment body. Mentioning users is not supported.

    - **`user` (required)**

      `object` — Author of the comment.

      - **`id` (required)**

        `integer` — Unique identifier for the user.

      - **`name` (required)**

        `string` — Display name of the user.

      - **`profile_image_url`**

        `object` — URL to the user's profile image. Null if not available.

  - **`draft_id` (required)**

    `integer` — Identifier of the draft this comment thread belongs to.

  - **`id` (required)**

    `string`, format: `uuid` — Unique identifier for the comment thread.

  - **`platform` (required)**

    `string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"` — Platform the comment thread is anchored on.

  - **`status` (required)**

    `string`, possible values: `"unresolved", "resolved"` — Resolution status of the comment thread.

  - **`selected_text`**

    `object` — The text the comment thread was originally anchored to. Captured when the thread was created and not re-derived afterwards, so it may not match the current post text if the post has been edited. Null for threads that were created against a whole post rather than a specific span.

- **`next`**

  `object` — URL for the next page (null on last page).

- **`previous`**

  `object` — URL for the previous page (null on first page).

**Example:**

```json
{
  "results": [
    {
      "id": "",
      "draft_id": 1,
      "platform": "x",
      "status": "unresolved",
      "selected_text": "",
      "comments": []
    }
  ],
  "count": 1,
  "limit": 1,
  "offset": 1,
  "next": "",
  "previous": ""
}
```

### CommentThreadResponse

- **Type:**`object`

A comment thread (one root comment plus follow-up comments) on a draft.

- **`comments` (required)**

  `array` — Comments in the thread, ordered by \`created\_at\`.

  **Items:**

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the comment was created (ISO 8601 in UTC).

  - **`id` (required)**

    `string`, format: `uuid` — Unique identifier for the comment.

  - **`text` (required)**

    `string` — Plain-text comment body. Mentioning users is not supported.

  - **`user` (required)**

    `object` — Author of the comment.

    - **`id` (required)**

      `integer` — Unique identifier for the user.

    - **`name` (required)**

      `string` — Display name of the user.

    - **`profile_image_url`**

      `object` — URL to the user's profile image. Null if not available.

- **`draft_id` (required)**

  `integer` — Identifier of the draft this comment thread belongs to.

- **`id` (required)**

  `string`, format: `uuid` — Unique identifier for the comment thread.

- **`platform` (required)**

  `string`, possible values: `"x", "linkedin", "mastodon", "threads", "bluesky", "substack", "x_article"` — Platform the comment thread is anchored on.

- **`status` (required)**

  `string`, possible values: `"unresolved", "resolved"` — Resolution status of the comment thread.

- **`selected_text`**

  `object` — The text the comment thread was originally anchored to. Captured when the thread was created and not re-derived afterwards, so it may not match the current post text if the post has been edited. Null for threads that were created against a whole post rather than a specific span.

**Example:**

```json
{
  "id": "",
  "draft_id": 1,
  "platform": "x",
  "status": "unresolved",
  "selected_text": "",
  "comments": [
    {
      "id": "",
      "text": "",
      "created_at": "",
      "user": null
    }
  ]
}
```

### CommentUserResponse

- **Type:**`object`

Author shape attached to a comment.

- **`id` (required)**

  `integer` — Unique identifier for the user.

- **`name` (required)**

  `string` — Display name of the user.

- **`profile_image_url`**

  `object` — URL to the user's profile image. Null if not available.

**Example:**

```json
{
  "id": 1,
  "name": "",
  "profile_image_url": ""
}
```

### CommentThreadCreateRequest

- **Type:**`object`

Create a new comment thread anchored on a span of a draft post.

- **`post_index` (required)**

  `integer` — Zero-based index of the target post within the platform's posts array.

- **`selected_text` (required)**

  `string` — Exact substring (codepoint-equal) of the target platform's visible flat text the comment thread is anchored to. Copy verbatim from the GET response. LinkedIn mentions appear inside \`posts\[\*].text\` as \`@\[Name]\(urn:li:organization:ID)\` or \`@\[Name]\(urn:li:person:ID)\`. Mentions are indivisible — \`selected\_text\` may either include the entire mention substring or stay outside it. A selection that starts or ends in the middle of a mention is rejected with \`400 VALIDATION\_ERROR\`.

- **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

- **`occurrence`**

  `integer`, default: `0` — Zero-based occurrence of \`selected\_text\` within the post when the same substring appears multiple times.

- **`platform`**

  `object` — Required when the draft has multiple commentable platforms; otherwise resolves to the source platform.

**Example:**

```json
{
  "post_index": 0,
  "platform": "x",
  "selected_text": "",
  "occurrence": 0,
  "text": ""
}
```

### XArticleCommentThreadCreateRequest

- **Type:**`object`

Create a new comment thread anchored on visible X Article text.

- **`platform` (required)**

  `string`, possible values: `"x_article"` — Use \`x\_article\` to anchor the comment thread on the draft article.

- **`selected_text` (required)**

  `string` — Exact substring (codepoint-equal) of the article's rendered visible text. Markdown syntax, media tags, and X post embed tags are not part of the match text. X Article text comments cannot overlap existing X Article text comments in this API version.

- **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

- **`occurrence`**

  `integer`, default: `0` — Zero-based occurrence of \`selected\_text\` within the article when the same substring appears multiple times.

- **`post_index`**

  `object` — Omit for X Article comments. If supplied with \`platform: "x\_article"\`, it must be 0.

**Example:**

```json
{
  "post_index": 0,
  "platform": "x_article",
  "selected_text": "",
  "occurrence": 0,
  "text": ""
}
```

### CommentCreateRequest

- **Type:**`object`

Add a comment to an existing comment thread.

- **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

**Example:**

```json
{
  "text": ""
}
```

### CommentUpdateRequest

- **Type:**`object`

Update the text body of a single comment within a comment thread.

- **`text` (required)**

  `string` — Plain-text comment body. The server derives the stored rich\_text from this.

**Example:**

```json
{
  "text": ""
}
```

### WebhookEventPayload

- **Type:**`object`

Payload envelope sent to webhook endpoints.

All webhook events follow this structure, with the event type identifying the specific event and data containing the full draft details.

- **`data` (required)**

  `object` — The draft data at the time of the event. See DraftDetailResponse schema for full structure.

  - **`created_at` (required)**

    `string`, format: `date-time` — Timestamp when the draft was created (ISO 8601 format in UTC)

  - **`draft_id` (required)**

    `integer` — Deprecated: Use 'id' instead. Unique identifier for the draft.

  - **`id` (required)**

    `integer` — Unique identifier for the draft

  - **`platforms` (required)**

    `object` — Platform configurations showing which platforms are enabled and their content

    - **`bluesky`**

      `object` — Bluesky configuration

    - **`linkedin`**

      `object` — LinkedIn configuration

    - **`mastodon`**

      `object` — Mastodon configuration

    - **`substack`**

      `object` — Substack Notes configuration

    - **`threads`**

      `object` — Threads configuration

    - **`x`**

      `object` — X (Twitter) configuration

    - **`x_article`**

      `object` — X Article configuration

  - **`preview` (required)**

    `string` — Text preview of the draft, smart-trimmed with a 100-character limit

  - **`private_url` (required)**

    `string` — Private URL for accessing the draft in Typefully. Can be shared with team members without requiring public sharing.

  - **`social_set_id` (required)**

    `integer` — ID of the social set (account) this draft belongs to

  - **`status` (required)**

    `string`, possible values: `"draft", "scheduled", "published", "publishing", "error", "planned"` — Current status of the draft. 'draft' = saved but not scheduled. 'scheduled' = queued to auto-publish at its scheduled\_date. 'planned' = dated but inert: it has a scheduled\_date but will NOT auto-publish until confirmed (by setting publish\_at). A planned draft whose scheduled\_date has passed is NOT overdue and NOT a failure - it simply hasn't been confirmed; replan it or confirm it. 'publishing' = a publish is in flight (transient). 'published' = successfully posted. 'error' = publishing failed. This reflects the stored draft lifecycle; it does not flip to 'publishing' while an immediate publish is in flight - use \`publish\_state\` to track that.

  - **`bluesky_post_published_at`**

    `object` — Timestamp when the post was published to Bluesky (ISO 8601 format in UTC). Null if not published to Bluesky.

  - **`bluesky_published_url`**

    `object` — URL of the published post on Bluesky. Null if not published to Bluesky or URL not available.

  - **`draft_title`**

    `object` — Human-readable title for the draft. This is for internal organization only and is not posted to social media.

  - **`linkedin_post_published_at`**

    `object` — Timestamp when the post was published to LinkedIn (ISO 8601 format in UTC). Null if not published to LinkedIn.

  - **`linkedin_published_url`**

    `object` — URL of the published post on LinkedIn. Null if not published to LinkedIn or URL not available.

  - **`mastodon_post_published_at`**

    `object` — Timestamp when the post was published to Mastodon (ISO 8601 format in UTC). Null if not published to Mastodon.

  - **`mastodon_published_url`**

    `object` — URL of the published post on Mastodon. Null if not published to Mastodon or URL not available.

  - **`publish_state`**

    `object` — Async publish-progress signal, separate from \`status\`. null = no publish initiated; 'in\_progress' = at least one platform is currently being posted; 'finished' = publishing has completed for all platforms. 'finished' means the job is done, not that it succeeded - read \`status\` and the per-platform published URLs (x\_published\_url, etc.) for the outcome. After publish\_at="now", poll GET /drafts/{id} until publish\_state is 'finished'.

  - **`published_at`**

    `object` — Timestamp when the draft was published on any enabled platform (ISO 8601 format in UTC). Null if not yet published.

  - **`scheduled_date`**

    `object` — Timestamp when the draft is scheduled to publish - or, for planned drafts, the inert planned date (ISO 8601 in UTC). Null if the draft has no date.

  - **`scratchpad_text`**

    `object` — Plain text scratchpad notes for the draft. Formatting is stripped.

  - **`share_url`**

    `object` — Public share URL for the draft. Null if the draft is not publicly shared.

  - **`substack_post_published_at`**

    `object` — Timestamp when the note was published to Substack (ISO 8601 format in UTC). Null if not published to Substack.

  - **`substack_published_url`**

    `object` — URL of the published note on Substack. Null if not published to Substack or URL not available.

  - **`tags`**

    `array` — List of tag slugs (not names) associated with this draft. Use the /tags endpoint to get available tags with their slugs.

    **Items:**

    `string`

  - **`threads_post_published_at`**

    `object` — Timestamp when the post was published to Threads (ISO 8601 format in UTC). Null if not published to Threads.

  - **`threads_published_url`**

    `object` — URL of the published post on Threads. Null if not published to Threads or URL not available.

  - **`updated_at`**

    `object` — Timestamp when the draft was last edited (ISO 8601 format in UTC). Null if never edited.

  - **`x_article_published_at`**

    `object` — Timestamp when the X Article was published (ISO 8601 format in UTC). Null if not published.

  - **`x_article_published_url`**

    `object` — URL of the published X Article. Null if not published or URL not available.

  - **`x_post_published_at`**

    `object` — Timestamp when the post was published to X (ISO 8601 format in UTC). Null if not published to X.

  - **`x_published_url`**

    `object` — URL of the published post on X (Twitter). Null if not published to X or URL not available.

- **`event` (required)**

  `string`, possible values: `"draft.created", "draft.scheduled", "draft.planned", "draft.published", "draft.status_changed", "draft.tags_changed", "draft.deleted"` — The event type that triggered this webhook

**Example:**

```json
{
  "event": "draft.created",
  "data": {
    "id": 12345,
    "social_set_id": 67890,
    "status": "draft",
    "publish_state": null,
    "created_at": "2025-01-15T10:30:00Z",
    "updated_at": "2025-01-16T09:15:00Z",
    "scheduled_date": "2025-01-20T14:00:00Z",
    "published_at": "2025-01-20T14:00:05Z",
    "draft_title": "Weekly Newsletter",
    "tags": [
      "marketing",
      "product"
    ],
    "preview": "Excited to announce our new feature! 🚀",
    "share_url": "https://typefully.com/share/abc123",
    "private_url": "https://typefully.com/?d=12345&a=67890",
    "platforms": null,
    "x_published_url": "https://x.com/username/status/1234567890",
    "linkedin_published_url": "https://www.linkedin.com/feed/update/urn:li:share:1234567890",
    "mastodon_published_url": "https://mastodon.social/@username/1234567890",
    "threads_published_url": "https://www.threads.net/@username/post/ABC123",
    "bluesky_published_url": "https://bsky.app/profile/username.bsky.social/post/abc123",
    "substack_published_url": "https://substack.com/@username/note/c-123456789",
    "x_article_published_url": "https://x.com/i/article/1234567890",
    "x_post_published_at": "2025-01-20T14:00:05Z",
    "linkedin_post_published_at": "2025-01-20T14:00:08Z",
    "mastodon_post_published_at": "2025-01-20T14:00:10Z",
    "threads_post_published_at": "2025-01-20T14:00:12Z",
    "bluesky_post_published_at": "2025-01-20T14:00:15Z",
    "substack_post_published_at": "2025-01-20T14:00:15Z",
    "x_article_published_at": "2025-01-20T14:00:15Z",
    "scratchpad_text": "line 1\nline 2\n\nline 4"
  }
}
```
