---
title: "Publish content to CMS"
method: POST
path: "/content/{id}/publish"
tags: ["Content"]
---

# Publish content to CMS

`POST /content/{id}/publish`

## Request

- Base URL: `https://next.frase.io/api/v1`
- URL: `https://next.frase.io/api/v1/content/{id}/publish`
- Auth: one of:
  - API key in header `X-API-KEY`
  - HTTP bearer

Publish content to a connected CMS platform (Frase CMS, WordPress, Webflow, or Sanity). The content must exist and the CMS must be connected to the specified site.

## Path parameters

- `id` string, uuid, required

## Request body

- object
  - `destination` 'frase_cms' | 'wordpress' | 'webflow' | 'sanity', required: Target CMS platform
  - `site_id` string, uuid, required: Site ID with the CMS connection
  - `status` 'publish' | 'draft': Publish status. DEFAULT IS "publish" (goes live) - pass "draft" to save without publishing. Cannot be combined with scheduled_at (400): the server schedules a LIVE publish whenever scheduled_at is set.
  - `scheduled_at` string, date-time: UTC (Z-suffixed) ISO 8601 datetime. Offset forms (+02:00) are rejected (400).
  - `force` boolean: Publish even if the content has rule warnings (rule ERRORS still block).

## Response `200`

Content published successfully

- object
  - `data` object
    - `success` boolean
    - `destination` string
    - `url` string, nullable: Live URL of the published item. For frase_cms this is derived from the site's CURRENT CMS settings (subdomain, active custom domain, or subdirectory host) and is the same value stored on the content row and sent in the content.published webhook; null when no domain is configured yet.
    - `external_id` string, nullable: External CMS item ID
    - `live_publish_failed` boolean: Webflow only, present only when true. The CMS item was written but Webflow refused the site-level publish, so NOTHING is live and `url` is null. The write itself succeeded, which is why this is a 200 and not an error — but the item is not readable until the site is published in Webflow. See `live_publish_message` for the reason.
    - `live_publish_message` string: Present alongside `live_publish_failed`: what still has to happen for the item to go live, including Webflow's own sentence where it gave a usable one.

## Other responses

- `400`: Validation failure: status draft combined with scheduled_at, or a non-UTC/malformed scheduled_at
- `401`: Authentication failed
- `403`: Forbidden - action not allowed on this resource
- `404`: Resource not found
- `409`: State conflict. Either the content type is review-gated and the post is not approved yet (review_required), or a scheduled publish was requested for an article Frase is still writing (still_generating) - arming a schedule captures the article body, so a half-written article would publish empty. Both clear on their own: approve the post, or wait for generation to finish.
- `422`: Validation failed

---

[API](https://skmtc.dev/frase/apis/frase-api.md) · [All operations](https://skmtc.dev/frase/apis/frase-api/llms.txt) · [OpenAPI document](https://skmtc.dev/frase/apis/frase-api/revisions/7fb75d89f7a4?raw)
