> ## Documentation Index
> Fetch the complete documentation index at: https://grow-hub.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Publishing tools

> Post now, schedule for later, or pull a scheduled post back.

## publish\_post

Publishes a draft immediately and returns the updated post with
`externalPostId` set.

<ParamField body="id" type="string" required>
  Post id. Must be in `draft` status.
</ParamField>

<ParamField body="platform" type="enum">
  `linkedin` or `x`. Omit when the draft targets exactly one platform.
</ParamField>

Before dispatching, Studio checks that the target is in `targetPlatforms`, the
content fits the platform's limit, attached assets are publishable, and the
platform is connected — refreshing the access token if it is close to expiry.

<Warning>
  Publishing is immediate and cannot be undone from Studio. Confirm with the user
  before calling it.
</Warning>

## schedule\_post

Schedules a post for future dispatch. Also re-schedules an already-scheduled post
and retries a failed one.

<ParamField body="id" type="string" required>
  Post id. Must be `draft`, `scheduled`, or `failed`.
</ParamField>

<ParamField body="scheduledAt" type="string" required>
  ISO 8601 timestamp **with offset**, e.g. `2026-09-01T09:00:00+05:30` or
  `2026-09-01T03:30:00Z`. Must be in the future.
</ParamField>

<ParamField body="platform" type="enum">
  Target platform. Omit when the post targets exactly one.
</ParamField>

Scheduling fails fast on a past timestamp, a missing connection, or over-length
content. The resolved target is stored as `scheduledPlatform`.

<Note>
  A cron tick runs every minute, so a post fires within roughly two minutes of
  its target time. Studio always schedules itself — it never hands a post to a
  platform's native scheduler — so one system holds the truth about what is
  pending.
</Note>

### What happens at fire time

1. The cron claims due posts atomically (`scheduled` → `queued`) and enqueues them.
2. The queue consumer re-validates against the database and publishes through the
   same path as `publish_post`.
3. Permanent errors (`not_connected`, `token_expired`, validation, platform 4xx)
   mark the post `failed` immediately — retrying can't fix them.
4. Transient errors (rate limits, platform 5xx) retry with exponential backoff up
   to 5 attempts, then mark it `failed` with the reason.

## unschedule\_post

Cancels a scheduled post, or recovers a failed one, back to `draft`. Clears
`scheduledAt`, `scheduledPlatform`, and `failureReason`.

<ParamField body="id" type="string" required>
  Post id. Must be `scheduled` or `failed`.
</ParamField>

Use this before retargeting a scheduled post — `targetPlatforms` is only editable
on a draft.
