> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mallary.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Edit Scheduled Post

> Change a scheduled post before any platform starts publishing it.

First, [get the post](/api-reference/endpoint/get-scheduled-post). Check that `data.editable` is `true`, and copy `data.revision`. Then send that number as `expected_revision` with at least one field to change. Fields you leave out stay the same.

You can change the text, media, first comments, publish time and time zone, auto-reply setting, platform options, and destinations. One edit changes the whole post group. The scheduled time must still be in the future, and no platform in the group may have started publishing.

## Change the text

```bash theme={null}
curl -X PATCH 'https://mallary.ai/api/v1/posts/12345' \
  -H "Authorization: Bearer $MALLARY_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"expected_revision":1,"message":"Updated launch reminder"}'
```

Replace `1` with the revision from the GET response. A successful edit returns the updated post and its new revision.

## Change the profile or platforms

Send `destinations` with **both** the target profile's public `profile_id` and the **complete final** platform list. Every platform in the group uses that one profile. A platform left out of the list is removed from the scheduled post.

```json theme={null}
{
  "expected_revision": 1,
  "destinations": {
    "profile_id": "AbC123xYz90",
    "platforms": ["instagram", "linkedin"]
  }
}
```

Use [List Connection Profiles](/api-reference/endpoint/profiles) to find profile IDs and the platforms connected to each profile. If you send `profile_id` in the URL query, it identifies the post's **current** profile. Put the new profile in `destinations.profile_id`.

If you change only the platforms, send the current profile ID in `destinations.profile_id`. When you move to another profile, Mallary clears saved account choices from the old profile, such as a LinkedIn author or Pinterest board. Send new `platform_options` if you need those choices.

When sent, `media`, `comments_under_post`, and `platform_options` replace their saved values. Send an empty array to clear media or first comments, or an empty object to clear platform options.

If the API returns `409`, get the post again before making another edit. The post may have changed or started publishing.


## OpenAPI

````yaml PATCH /api/v1/posts/{id}
openapi: 3.0.3
info:
  title: Mallary API
  version: 1.5.0
  description: >
    Public API for multi-platform publishing, analytics, media upload, and
    webhooks.


    Authentication:

    - Use header `Authorization: Bearer {api_key}`.

    - API key identity is authoritative. Do not use `user_id` to identify a
    caller.


    Connection profiles:

    - Omit `profile_id` to use the authenticated user's default profile.

    - Send the profile's random public `profile_id` when you want to publish,
    list posts, read post analytics or audience counts, list platforms, update
    settings, or disconnect a platform for a non-default profile.

    - Use `GET /api/v1/profiles` to list profile IDs.

    - Per-platform account caps by plan: Free 1, Starter 4, Pro 10, Business 50.


    Rate limits (per user, per minute) depend on subscription plan:

    - plan_id 1: 75 req/min

    - plan_id 2: 150 req/min

    - plan_id 3: 750 req/min

    - plan_id 4: 1500 req/min
servers:
  - url: https://mallary.ai
    description: Production
security:
  - bearerAuth: []
externalDocs:
  description: Full product docs
  url: https://docs.mallary.ai/
paths:
  /api/v1/posts/{id}:
    patch:
      summary: Edit a scheduled post before publishing starts
      description: >-
        Updates the entire grouped post. Omitted fields keep their current
        values. To change platforms or the connection profile, send
        `destinations` with both the target public `profile_id` and the complete
        final `platforms` list. The scheduled time must be in the future, and no
        platform in the group may have started publishing. A stale revision
        returns HTTP 409.
      operationId: updateScheduledPost
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
        - in: query
          name: profile_id
          required: false
          schema:
            $ref: '#/components/schemas/ProfileId'
          description: >-
            Optional current profile of the post. The target profile belongs in
            `destinations.profile_id`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateScheduledPostRequest'
      responses:
        '200':
          description: Scheduled post updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetScheduledPostResponse'
        '400':
          description: >-
            Invalid content, time, media, platform options, destination list, or
            target connection
        '401':
          description: Missing or invalid API key
        '402':
          description: Posting allowance reached when adding destinations
        '403':
          description: >-
            Scheduling or auto-reply not available on the current plan, posting
            paused, or content policy blocked
        '404':
          description: Post or target connection profile not found
        '409':
          description: Revision changed, post no longer editable, or upload not complete
        '429':
          description: Rate limit or post safety limit exceeded
components:
  schemas:
    ProfileId:
      type: string
      pattern: ^[A-Za-z0-9]{6,32}$
      example: AbC123xYz90
      description: Random public connection profile ID returned by `GET /api/v1/profiles`.
    UpdateScheduledPostRequest:
      type: object
      required:
        - expected_revision
      properties:
        expected_revision:
          type: integer
          minimum: 1
          description: >-
            Revision returned by `GET /api/v1/posts/{id}`. A stale value returns
            HTTP 409.
        message:
          type: string
          minLength: 1
        media:
          type: array
          description: Replaces all saved media. Send an empty array to remove media.
          items:
            $ref: '#/components/schemas/MediaItem'
        comments_under_post:
          type: array
          maxItems: 3
          description: Replaces all saved comments. Send an empty array to remove comments.
          items:
            oneOf:
              - type: string
              - type: object
                required:
                  - content
                properties:
                  content:
                    type: string
        scheduled_at:
          type: string
          description: >-
            New future publish time. Send an absolute time or a local time with
            `scheduled_timezone`.
        scheduled_timezone:
          type: string
          nullable: true
          description: >-
            IANA timezone. Send null to clear it when `scheduled_at` is
            absolute.
        auto_reply_enabled:
          type: boolean
        platform_options:
          type: object
          nullable: true
          description: >-
            Complete replacement map of per-platform options. Send an empty
            object or null to clear all overrides.
          additionalProperties:
            type: object
        destinations:
          $ref: '#/components/schemas/UpdateScheduledPostDestinations'
    GetScheduledPostResponse:
      type: object
      properties:
        status:
          type: string
          enum:
            - ok
        data:
          $ref: '#/components/schemas/ScheduledPostDetail'
    MediaItem:
      type: object
      required:
        - url
      properties:
        url:
          type: string
          format: uri
          description: Media URL (`mediaUrl` must be obtained from `/api/v1/upload`).
        type:
          type: string
          description: Optional MIME type hint.
        thumbnail_url:
          type: string
          format: uri
          description: >-
            Optional Mallary-hosted image URL to use as the custom thumbnail or
            cover for a video post when supported by the target platform.
        alt_text:
          type: string
          maxLength: 2000
          description: >-
            Optional media description for accessibility. Bluesky adds it to the
            matching image or video.
    UpdateScheduledPostDestinations:
      type: object
      required:
        - profile_id
        - platforms
      properties:
        profile_id:
          $ref: '#/components/schemas/ProfileId'
          description: The target connection profile for the whole grouped post.
        platforms:
          type: array
          minItems: 1
          uniqueItems: true
          description: >-
            Complete final platform list. Platforms omitted from this list are
            removed from the scheduled post.
          items:
            $ref: '#/components/schemas/Platform'
    ScheduledPostDetail:
      type: object
      required:
        - id
        - revision
        - editable
        - profile_id
        - platforms
        - message
        - media
        - scheduled_at
      properties:
        id:
          type: integer
          description: >-
            Stable numeric ID for this grouped post, including after
            destinations change.
        batch_id:
          type: string
          nullable: true
        job_ids:
          type: array
          description: Active Mallary job IDs for the current destinations.
          items:
            type: integer
        revision:
          type: integer
          minimum: 0
        editable:
          type: boolean
        edit_block_reason:
          type: string
          nullable: true
        profile_id:
          type: string
          nullable: true
          description: >-
            Public connection profile ID, or null if that profile no longer
            exists.
        platforms:
          type: array
          items:
            $ref: '#/components/schemas/Platform'
        destinations:
          type: array
          items:
            $ref: '#/components/schemas/ScheduledPostDestination'
        message:
          type: string
        media:
          type: array
          items:
            $ref: '#/components/schemas/MediaItem'
        comments_under_post:
          type: array
          maxItems: 3
          items:
            type: object
            properties:
              content:
                type: string
        scheduled_at:
          type: string
          nullable: true
          description: Saved scheduled publish time.
        scheduled_timezone:
          type: string
          nullable: true
        auto_reply_enabled:
          type: boolean
        platform_options:
          $ref: '#/components/schemas/PlatformOptions'
    Platform:
      type: string
      description: Supported publishing platform identifier.
      enum:
        - twitter
        - x
        - facebook
        - instagram
        - linkedin
        - youtube
        - tiktok
        - pinterest
        - reddit
        - threads
        - bluesky
    ScheduledPostDestination:
      type: object
      properties:
        job_id:
          type: integer
        platform:
          $ref: '#/components/schemas/Platform'
        profile_id:
          type: string
          nullable: true
          description: >-
            Public connection profile ID, or null if that profile no longer
            exists.
        account_name:
          type: string
          nullable: true
        account_handle:
          type: string
          nullable: true
    PlatformOptions:
      type: object
      description: Per-platform options keyed by platform id.
      additionalProperties: true
      properties:
        facebook:
          type: object
          properties:
            message:
              type: string
              description: >-
                Facebook-specific message/caption. Defaults to the top-level
                `message`.
            post_type:
              type: string
              enum:
                - feed
                - story
                - reel
              default: feed
              description: >-
                Defaults to `feed` when omitted. `story` publishes one
                image/video as a Facebook Story without caption/follow-up
                comments. `reel` publishes one 9:16 MP4/MOV video that is 3 to
                90 seconds long.
            link:
              type: string
              format: uri
              description: Optional link for a feed post without media.
            pageId:
              type: string
              description: Optional advanced override for a connected Facebook Page.
            title:
              type: string
              maxLength: 255
              description: Optional title for a Facebook Reel.
        instagram:
          type: object
          properties:
            message:
              type: string
              description: Instagram-specific caption. Defaults to the top-level `message`.
            post_type:
              type: string
              enum:
                - feed
                - story
                - reel
                - carousel
              default: feed
              description: >-
                Defaults to `feed` when omitted. `story` publishes one
                image/video as an Instagram Story without caption/follow-up
                comments, `reel` requires one video, and `carousel` publishes 2
                to 10 image/video items as one carousel post.
            shareToFeed:
              type: boolean
              default: true
              description: >-
                For Reels only. When true, the Reel appears in the profile feed
                and Reels tab. When false, it appears only in the Reels tab.
            trialParams:
              type: object
              description: >-
                For Reels only. Add this object to publish the video as a Trial
                Reel that Instagram shows to non-followers first. Do not use
                `shareToFeed` with a Trial Reel.
              required:
                - graduationStrategy
              additionalProperties: false
              properties:
                graduationStrategy:
                  type: string
                  enum:
                    - MANUAL
                    - SS_PERFORMANCE
                  description: >-
                    Use `MANUAL` to decide later in Instagram, or
                    `SS_PERFORMANCE` to let Instagram share the Reel with
                    followers if it performs well.
            isPaidPartnership:
              type: boolean
              description: >-
                Show Instagram's Paid partnership label. Available for feed
                posts, Reels, and carousels connected through Facebook Login.
                Sponsors also turn this on.
            brandedContentSponsors:
              type: array
              maxItems: 2
              description: >-
                Up to two sponsor Instagram usernames or numeric user IDs. Each
                sponsor must be a public Business or Creator account. Available
                only through Facebook Login and not for Stories.
              items:
                type: string
                pattern: ^@?(?:[A-Za-z0-9._]{1,30}|\d{1,64})$
        threads:
          type: object
          properties:
            message:
              type: string
              description: Threads-specific message. Defaults to the top-level `message`.
            post_type:
              type: string
              enum:
                - text
                - image
                - video
                - carousel
              description: >-
                Defaults dynamically. Mallary uses `text` when no media is
                supplied, `carousel` when more than one supported media item is
                supplied, `video` for a single MP4/MOV item, and `image` for a
                single JPG/JPEG/PNG/WEBP item.
        twitter:
          type: object
          properties:
            message:
              type: string
              description: X-specific message. Defaults to the top-level `message`.
        x:
          type: object
          properties:
            message:
              type: string
              description: X-specific message. Defaults to the top-level `message`.
        linkedin:
          type: object
          properties:
            message:
              type: string
              description: LinkedIn-specific message. Defaults to the top-level `message`.
            author_urn:
              type: string
              description: Optional author URN override.
        tiktok:
          type: object
          properties:
            message:
              type: string
              description: >-
                TikTok-specific message/caption. Defaults to the top-level
                `message`.
            post_type:
              type: string
              enum:
                - video
                - photo
              description: >-
                Defaults dynamically. Mallary picks `photo` when the media list
                contains only images and no videos; otherwise it uses `video`.
            media_type:
              type: string
              enum:
                - PHOTO
              description: >-
                Optional alias for selecting the TikTok photo variant. No
                default; use this only when you want to force photo mode via the
                alias.
            post_mode:
              type: string
              enum:
                - DIRECT_POST
                - MEDIA_UPLOAD
              default: DIRECT_POST
              description: >-
                Defaults to `DIRECT_POST`, which publishes through Mallary. Use
                `MEDIA_UPLOAD` only when you want to send the upload to the
                creator inbox and finish publishing it in TikTok.
            source:
              type: string
              enum:
                - FILE_UPLOAD
                - PULL_FROM_URL
              description: >-
                Defaults by variant. Video posts default to `FILE_UPLOAD`. Photo
                posts always use `PULL_FROM_URL`. Mallary only accepts
                `PULL_FROM_URL` media from its own CDN.
            title:
              type: string
              maxLength: 2200
              description: >-
                TikTok caption/title override. Defaults to `message` (trimmed to
                TikTok's limit). For photo posts, TikTok applies a shorter
                platform limit.
            description:
              type: string
              maxLength: 4000
              description: >-
                TikTok photo description override. No default; omitted unless
                supplied.
            privacy_level:
              type: string
              enum:
                - PUBLIC_TO_EVERYONE
                - MUTUAL_FOLLOW_FRIENDS
                - FOLLOWER_OF_CREATOR
                - SELF_ONLY
              description: >-
                Applies to `DIRECT_POST`. Defaults to the first allowed privacy
                level returned by TikTok creator info, preferring
                `PUBLIC_TO_EVERYONE`, then `MUTUAL_FOLLOW_FRIENDS`, then
                `FOLLOWER_OF_CREATOR`, then `SELF_ONLY`. If TikTok returns the
                private-account-only direct-post restriction, Mallary retries
                once with the most private allowed level.
            disable_comment:
              type: boolean
              description: >-
                Controls whether comments are disabled on the TikTok post.
                Applies to direct posts. When omitted, Mallary falls back to the
                creator's current TikTok comment setting.
            disable_duet:
              type: boolean
              description: >-
                Controls whether other TikTok users can create Duets from the
                video. Applies to direct-post video only. When omitted, Mallary
                falls back to the creator's current TikTok duet setting.
            disable_stitch:
              type: boolean
              description: >-
                Controls whether other TikTok users can create Stitches from the
                video. Applies to direct-post video only. When omitted, Mallary
                falls back to the creator's current TikTok stitch setting.
            video_cover_timestamp_ms:
              type: integer
              minimum: 0
              description: >-
                Millisecond timestamp used to choose the video cover frame for a
                direct-post TikTok video. Omitted unless supplied.
            auto_add_music:
              type: boolean
              description: >-
                For direct-post photo posts, asks TikTok to automatically add
                music to the photo carousel. Omitted unless supplied.
            brand_content_toggle:
              type: boolean
              description: >-
                TikTok branded-content disclosure flag. When true, marks the
                post as branded content. Omitted unless supplied.
            brand_organic_toggle:
              type: boolean
              description: TikTok brand-organic disclosure flag. Omitted unless supplied.
            is_aigc:
              type: boolean
              description: >-
                TikTok AI-generated-content disclosure flag for direct-post
                video. Omitted unless supplied.
            photo_cover_index:
              type: integer
              minimum: 0
              default: 0
              description: >-
                Zero-based index of the image TikTok should use as the photo
                carousel cover. Values beyond the image count are clamped to the
                last image. Defaults to `0` when omitted.
        youtube:
          type: object
          properties:
            message:
              type: string
              description: >-
                YouTube-specific description/default-title source. Defaults to
                the top-level `message`.
            post_type:
              type: string
              enum:
                - regular
                - shorts
              description: >-
                Defaults dynamically. Mallary uses `shorts` when the video is
                short-form (<= 180s) and vertical, or when dimensions are
                unavailable for a short candidate; otherwise it uses `regular`.
            title:
              type: string
              maxLength: 100
              description: >-
                Defaults to the first non-empty value of
                `platform_options.youtube.title`, the first line of `message`,
                or `Untitled Video`.
            tags:
              type: array
              description: >-
                Optional video tags. Each tag can use up to 100 characters. The
                complete tag list can use up to 500 characters after YouTube
                counts commas and quote characters around tags with spaces.
                Exact duplicates are removed before upload.
              items:
                type: string
                minLength: 1
                maxLength: 100
                pattern: ^[^<>]*$
            visibility:
              type: string
              enum:
                - public
                - unlisted
                - private
              default: public
              description: Defaults to `public`.
            categoryId:
              type: string
              default: '22'
              description: Defaults to YouTube category id `22`.
            madeForKids:
              type: boolean
              default: false
              description: Defaults to `false`.
            containsSyntheticMedia:
              type: boolean
              description: >-
                Optional AI-content disclosure. Set to `true` when the video
                contains realistic AI-generated or altered content that could be
                mistaken for real people, places, or events. YouTube may show a
                disclosure label. Omitted unless supplied.
            playlist_id:
              type: string
              pattern: ^[A-Za-z0-9_-]{2,150}$
              description: >-
                Existing playlist owned by the connected YouTube channel.
                Mallary checks access before upload, then adds the new video to
                this playlist.
        pinterest:
          type: object
          properties:
            message:
              type: string
              description: >-
                Pinterest-specific description/default title source. Defaults to
                the top-level `message`. Mallary shortens descriptions longer
                than Pinterest's 800-character limit.
            post_type:
              type: string
              enum:
                - image
                - video
              description: >-
                Defaults dynamically. Mallary uses `video` when the media list
                contains a video; otherwise it uses `image`.
            title:
              type: string
              maxLength: 100
            boardId:
              type: string
              description: >-
                Board to post into. Defaults to the connected Pinterest board id
                from the saved token when omitted.
            link:
              type: string
              format: uri
            alt_text:
              type: string
              description: Short image description for people who use screen readers.
        reddit:
          type: object
          properties:
            message:
              type: string
              description: >-
                Reddit-specific title/text source. Defaults to the top-level
                `message`.
            post_type:
              type: string
              enum:
                - text
                - link
                - image
              description: >-
                Defaults dynamically. Mallary uses `image` when the media list
                contains an image; otherwise it uses `text`.
            subreddit:
              type: string
              description: Community that receives the post.
            subredditName:
              type: string
              description: Alias for `subreddit`.
        bluesky:
          type: object
          properties:
            message:
              type: string
              maxLength: 300
              description: Bluesky-specific message. Defaults to the top-level `message`.
            languages:
              type: array
              maxItems: 3
              description: Optional BCP 47 language codes for the post.
              items:
                type: string
            langs:
              type: array
              maxItems: 3
              description: Alias for `languages`.
              items:
                type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'Use `Authorization: Bearer {api_key}`'

````