POST /v1/brands/{brand_id}/content-briefs/{brief_id}/publish

Publish a content brief

Publish a finished brief and return it with its updated status.

Set target_type to pick where it goes:

  • cms_connection: push the article to a connected site (WordPress, Ghost, Shopify, Webflow, Wix, a webhook or a GitHub repository). Pass the connection id from GET /v1/brands/{brand_id}/cms-connections as target_id. Publishing the same brief to the same connection again updates the post in place rather than creating a duplicate. A GitHub repository gets a pull request instead: the publication answers draft with its review_url and goes live when the pull request merges (a repository set to commit directly takes a live send straight onto its branch and answers published with no review_url); a draft send answers draft with its draft pull request as review_url, and is refused (400) on a post already live there; scheduled, it opens nothing on GitHub until its time, so the repository holds nothing while it waits.
  • hosted_site: publish to the brand's RankPrompt-hosted blog, which must already be active.
  • external: record an external_url you published yourself, pushing nothing anywhere.

Use publish_status: draft to land the post as a CMS draft. scheduled_for queues the publish instead: the post waits on the site as a draft (the hosted blog keeps it in RankPrompt) and our queue makes it live at that time, retrying a site that does not answer. A draft cannot be scheduled: publish_status: draft with scheduled_for is a 400. A scheduled brief stays in needs_review until it actually goes live.

Publishing approves any open review on the article.

Path parameters

Query parameters

  • dry_run boolean optional

    Preview the call without executing it. Runs auth, scope, eligibility and body validation, then returns 200 with a dry_run object reporting the request-unit and credit cost plus a non-consuming quota snapshot. Nothing is debited, reserved, dispatched or mutated. An invalid body still returns 422.

    default: false

Header parameters

  • Idempotency-Key string required

    Required with an API key, else 400 idempotency_key_required: a new value per operation (a UUID works). A retry with the same key and body within 24 hours replays the first response instead of running again. Ignored with dry_run=true.

Request body

application/json required
Type: ContentBriefPublish
  • external_url string (nullable) nullable

    Canonical URL where the content was published. Only used when target_type is external.

  • Whether the CMS post goes live (publish) or is created as a draft for someone to review in the CMS (draft). Ignored for external.

    Left out, the post goes live even when the brand's owner has every send to that site start as a draft: read the target's default_status (GET .../publish-targets or GET .../cms-connections) and send draft when it says draft, unless the person asked for it live.

    default: `publish`

  • scheduled_for string (nullable) nullable

    Publish at this time instead of now, as an ISO-8601 datetime in the future. The post waits on the site as a draft (the hosted blog keeps it in RankPrompt) until our queue makes it live at that time, retrying a site that does not answer; the brief stays in needs_review until then. Not accepted with publish_status: draft.

    Scheduling a brief that is already published leaves the live post as it is until that time, then updates it.

    format: `date-time`

  • target_id string (nullable) nullable

    The CMS connection id, required for cms_connection. Leave it out for hosted_site, which is resolved from the brand, and for external.

  • Where to publish:

    • cms_connection: push the article to a connected destination (WordPress, Ghost, Shopify, Webflow, Wix, a GitHub repository or a webhook). Set target_id to the connection id from GET /v1/brands/{brand_id}/cms-connections.
    • hosted_site: publish to the brand's RankPrompt-hosted blog, which is resolved from the brand. The hosted site must already be active; setting one up needs interactive DNS in the app.
    • external: publish nothing, just mark the brief published and record external_url, for when you pushed the article somewhere else yourself.

    default: `external`

Responses

Publish a content brief application/json
Type: ContentBrief
  • brand_id string required

    Owning brand id (UUID).

  • created_at string required

    When the brief was created.

    format: `date-time`

  • id string required

    Content brief id (UUID).

  • status enum required

    Brief lifecycle state. One of draft, processing, needs_review, published, failed.

  • assets array of ContentAsset

    Generated assets (article HTML, images, meta).

  • The format's own fields (the ones a content opportunity pre-fills, e.g. listTheme and listSize for ranked_list), plus sources: {"urls": [...], "only_mine": false}, up to 10 pages or PDFs the article reads before writing and cites, each reported in quality_report.enforcement.sources as used, not_used or source_unreadable (only_mine: true turns web search off and unwraps any other outside link), sourcePrompts: ids of the report prompts the article answers, and length: {"mode": "auto"|"short"|"standard"|"long"} or {"mode": "exact", "words": 300-5000} (omitted: the brand's default_length), and blockedWords: up to 100 phrases of up to 100 characters this article must not use on top of the brand's banned_phrases, and knowledge: {"mode": "auto"} (the knowledge base chunks closest to the topic, the default), {"mode": "pick", "ids": [...]} (only those knowledge base items) or {"mode": "off"}. This article only: language (a language name, over the brand's), tone (up to 200 characters, over the brand's), audience (who it is written for, up to 200 characters) and tools ({tool: on|off|auto} over the brand's AI tools, see content preferences). Other keys are ignored; the brand's banned phrases stay in its preferences.

    Show fields

    Empty object (no properties).

  • content_type string (nullable) nullable

    Content format id (e.g. blog_post, ranked_list).

  • error_message string (nullable) nullable

    User-facing failure reason when status is failed.

  • generated_at string (nullable) nullable

    When content was last generated, if ever.

    format: `date-time`

  • generation_settings object | array of any

    What the last run wrote with and where each value came from: language, tone, length, audience, banned_phrases, knowledge and sources as {value, origin}, and tools as {value: {tool: mode}, origin: {tool: origin}}; origin is article (this brief's content_context), brand (the brand's content preferences) or system. A regeneration keeps the brand values it records unless asked for today's. Null before the first run.

  • mode enum

    create writes a new article; optimize improves the page at source_url (content-optimizations).

    default: `create`

  • object string

    Always content_brief.

    default: `content_brief`

  • opportunity_id string (nullable) nullable

    The content opportunity this article answers, when it was created from one.

  • optimize_level enum (nullable) nullable

    How far an optimize brief goes; null for create.

  • outline_json object | array of any

    Structured article outline produced during generation.

  • planned_publish_at string (nullable) nullable

    The day the article sits on in the content calendar until it is published or a publish is queued; null when it has none. Set it with PATCH /v1/brands/{brand_id}/content-briefs/{brief_id}.

    format: `date-time`

  • publications array of ContentPublication

    Every place the article was published, with its live URL. Empty until it is published.

  • quality_report object | array of any

    Rule-based checks run on the finished article: {score, axes[], checks[], notes[], words, language, link_check}, where score is the percentage of weighted checks that passed. link_check is the source check run after writing: checked (how many external links were fetched), removed[] (links unwrapped because the page is gone: url, reason, status), warnings[] (links kept but worth a look, each {url, reason}: moved_to_root when a deep link now lands on the site's home page, with the final_url it landed on, unverified when the research never read the page) and unverified_claims[] ({sentence, numbers[], urls[], reason}, reason being not_on_source when the cited page does not carry the figures, source_removed when every cited link was removed, source_unreadable when no cited page could be read). Advisory only; it never blocks publishing.

  • research_output string (nullable) nullable

    Research notes gathered during generation.

  • review_due_at string (nullable) nullable

    When the article's fastest-ageing fact (a price, a plan tier) is worth re-checking.

    format: `date-time`

  • serp_research object | array of any

    What Google ranked for the target keyword when the article was researched: pages (rank, url, domain, title, words, published_at, reachable), people_also_ask, features, table_stakes (subtopics most top pages cover), differentiators, median_words and intent (Google's main intent for the keyword: informational, commercial, transactional or navigational, null when unknown). Null when the results page was unavailable.

  • source_url string (nullable) nullable

    The brand's own page an optimize brief improves.

  • target_keywords array of string

    Target keywords for the brief.

  • title string (nullable) nullable

    Brief title, set on creation or filled in from the article.

  • topic string (nullable) nullable

    Topic or target keyword the content is written around.

  • updated_at string (nullable) nullable

    When the brief was last updated.

    format: `date-time`

Request validation failed application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
Missing or invalid credentials application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid credentials"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
You do not have permission to access this resource application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "forbidden",
    "message": "You do not have permission to access this resource"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
Resource not found application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "not_found",
    "message": "Resource not found"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
Resource conflict application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "conflict",
    "message": "Resource conflict"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
Request body did not match the expected schema application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "body_validation_error",
    "message": "Request body did not match the expected schema"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
Per-minute rate limit exceeded application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Per-minute rate limit exceeded"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
An unexpected error occurred application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}
Service temporarily unavailable, please retry application/json
Type: ApiError
  • error object required
    Show fields
    • code string required

      Stable machine-readable error code from the catalog.

    • message string required

      Human-readable description suitable for logging.

    • details object

      Optional, code-specific context (e.g. errors array for validation_error, retry_after for service_unavailable). Shape depends on error.code; treat as opaque otherwise.

      Show fields

      Empty object (no properties).

  • Server-generated correlation id for the request. Mirrors the X-Correlation-ID response header. Include this when contacting support.

    format: `uuid`

Example response
{
  "error": {
    "code": "service_unavailable",
    "message": "Service temporarily unavailable, please retry"
  },
  "request_id": "9f6c4e8a-1d3b-4c8b-9f1e-7a3b1c2d3e4f"
}