PUT
/v1/posts/slug/{slug}

Update an existing post using its URL slug. The publication is identified by the API key provided in the Authorization header.

Behavior:

  • Only provided fields are updated; omitted fields remain unchanged
  • When markdown is provided, it replaces the full content. Rich blocks (embeds, buttons, callouts) created in the editor will be lost
  • Set status to "published" to publish a draft, "draft" to unpublish, or "archived" to archive
  • When editing an already-live post that should remain live, include status: "published" in the update and verify the returned post status before telling the writer it is live
  • Set scheduledAt (Unix timestamp in milliseconds) to schedule a draft's first-publish for a future time. Must be in the future and at most 30 days out. Only valid for posts that haven't been published or already scheduled. Pass scheduledAt: null to cancel a previously scheduled publish (or to reschedule: cancel first, then schedule again with the new time). Set sendNewsletter: true alongside scheduledAt to email subscribers when the post publishes.
  • Set imageUrl to update the post's cover/hero image; the URL is fetched, re-hosted, and a placeholder is generated. Pass clearImage: true to remove the existing cover.
  • Set imageAlt to describe the cover image for screen readers. It renders everywhere the cover appears, so set it whenever you set imageUrl.

Authorization

apiKey
headerAuthorizationBearer <token>

API key for authenticating protected endpoints. Pass as Bearer token in Authorization header.

Path Parameters

slug*string

URL-friendly identifier of the post to update

Length1 <= length <= 256

Request Body

Body

application/json
  1. body
markdown?string

Post content in Markdown format. Replaces the FULL body. Markdown cannot represent buttons, linked images, or embedded media (videos, tweets, link cards) — replacing a post that has any of those with markdown drops them. Use bodyJson (round-tripped from get-post) to edit an existing post so nothing is lost. Provide markdown OR bodyJson, not both.

bodyJson?string

Post content as a Tiptap document, JSON-stringified (e.g. '{"type":"doc","content":[...]}'). This accepts ANY Tiptap node the editor supports — including videos, tweets, link cards, callouts, and buttons — so editing a post by round-tripping the json returned by get-post preserves everything markdown would drop. Replaces the FULL body; node-type validity is checked by the renderer and an unusable document is rejected. Provide markdown OR bodyJson, not both.

title?string

Title of the post

Lengthlength <= 200
subtitle?string

Optional subtitle or brief summary

Lengthlength <= 300
slug?string

URL-friendly identifier for the post

Length1 <= length <= 256
postPreview?string

Preview text for the post

Lengthlength <= 500
categories?array<string>|

Category tags for the post. Can also be a comma-separated string.

authorIds?array<string>

User ids credited as the post's authors, in byline order. Replaces the full list, so include every author you want kept — read the post's current authorIds first and start from those. Each id must be the publication's owner or an active team member; ids from outside the publication are rejected. There is no endpoint that enumerates members, so an id has to come from a post you have read (authorIds on a single post, or authors[].id on a list) or from the writer.

Items1 <= items <= 20
status?string

Set to 'published' to publish a draft or keep an already-live post published after edits, 'draft' to unpublish, or 'archived' to archive

Value in"draft""published""archived"
scheduledAt?|

Unix timestamp (milliseconds) to schedule the post's first publish at a future time. Must be in the future and at most 30 days out. Only valid for draft posts that haven't been published or already scheduled. Cannot be combined with status: 'draft' or 'archived'. Pass null to cancel a previously scheduled publish. The value 0 is treated the same as omitting the field (no scheduling request); note that on an already-scheduled post, omitting scheduledAt while setting status to 'published' or 'archived' cancels the schedule, and 'draft' keeps it.

Range0 <= value
sendNewsletter?|

Whether to send an email newsletter to subscribers when the post publishes. Only meaningful when publishing (status: 'published') or scheduling (scheduledAt set). Default: false

canonicalUrl?|

Canonical URL used in rendered metadata. Pass null to clear it. This does not change the post's Paragraph permalink.

Formaturi
Lengthlength <= 2048
publishOnline?boolean

Whether the post should be publicly visible online when published. Set false for newsletter-only delivery.

communityId?|

Subscriber segment id for newsletter delivery. Pass null for the general audience. Unavailable or cross-publication segments are rejected rather than broadened.

Length1 <= length <= 128
publishedAt?integer

Unix timestamp (milliseconds) to set as the post's publish date. Once set, the date is preserved across re-publishes.

Range0 <= value
imageUrl?string

URL of an image to set as the post's cover/hero image. The image is fetched, re-hosted on Paragraph's CDN, and a placeholder is generated. Pass clearImage: true instead to remove the existing cover.

Formaturi
clearImage?boolean

When true, removes the post's existing cover/hero image. Ignored if imageUrl is also provided.

imageAlt?|

Alternative text describing the cover image. Set it alongside imageUrl when replacing the image; on its own it re-describes the existing cover. Pass null or an empty string to clear it. Ignored when the post has no cover.

Lengthlength <= 1000

Response Body

Post updated successfully

application/json
  1. response
success*true

Whether the update succeeded

Value intrue
curl -X PUT "https://example.com/v1/posts/slug/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "success": true}