Docs · API · Publishing

Publishing.

One call pushes a finished article — content, slug, meta tags, featured image — into your CMS. Connect WordPress, Sanity, or Contentful once in the dashboard; publish forever via the API.

Integrations#

CMS credentials are configured at Dashboard → Integrations, where they are verified against the live CMS and stored encrypted (AES-256-GCM). There is deliberately no public API for creating integrations — credentials never need to transit your application code. Each integration gets an ID you reference when publishing.

Publish an article#

POST/v1/articles/{id}/publish
FieldTypeDescription
integration_idstringrequiredThe connected CMS to publish to. Must belong to your org, else 404.
as_draftbooleandefault: falsetrue creates the post in the CMS unpublished (WordPress draft status, Sanity without publishedAt, Contentful entry left unpublished).
Request
curl -X POST https://scribe.whizztech.ai/v1/articles/9e72c4b0-51af-4c3d-8e6a-2d94b7f01c55/publish \
  -H "Authorization: Bearer $SCRIBE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "integration_id": "e8d3b6a1-42f7-49c0-95ab-7c614f20d9e8",
    "as_draft": false
  }'
Response · 200 OK
{
  "object": "publish",
  "status": "published",
  "external_id": "1284",
  "external_url": "https://www.acme-analytics.com/blog/saas-churn-product-analytics"
}
FieldTypeDescription
object"publish"Resource type.
status"published"The publish attempt succeeded.
external_idstringPost/document/entry ID inside the CMS.
external_urlstring | undefinedPublic URL when the CMS returns one (WordPress does; Sanity and Contentful IDs resolve inside your own frontend).

A CMS-side failure returns 502 with code publish_failed and the upstream reason:

Response · 502 Bad Gateway
{
  "error": {
    "code": "publish_failed",
    "message": "WordPress publish failed (401): rest_cannot_create"
  }
}

Publishing marks the article published and records the attempt. Publishing the same article again creates a new post in the CMS — deduplicate on your side if you retry.

WordPress setup#

Uses the core REST API with Application Passwords — no plugin required. Requirements: WordPress ≥ 5.6, HTTPS, and a user with the Author role or higher (the connector verifies publish_posts capability at connect time).

  1. Log in to wp-admin as the publishing user and open Users → Profile → Application Passwords.
  2. Name it whizz-scribe and click Add New Application Password. Copy the generated password (spaces are fine — they're part of it).
  3. In Dashboard → Integrations, choose WordPress and enter the site URL (e.g. https://www.example.com), the username, and the application password. Verification calls /wp-json/wp/v2/users/me.

What a publish sends:

  • Title, slug, and the article HTML with the H1 stripped (WordPress renders the title).
  • meta_description as the excerpt.
  • The hero image (when present) uploaded to the media library, alt-texted, and set as the featured image.
  • Yoast and RankMath meta fields for SEO title/description — harmless when neither plugin is installed; if the meta write is rejected the connector automatically retries the post without it.

Sanity setup#

Writes documents through the Content Lake mutations API with a robot token. Body markdown is converted to Portable Text blocks: H2–H4 headings, bullet lists, and paragraphs (the H1 is dropped — the title lives on the document; links are flattened to plain text).

  1. In sanity.io/manage, open your project → API → Tokens and add a token with Editor permissions.
  2. In Dashboard → Integrations, enter the project ID, dataset (e.g. production), and the token. Optional: a document type — defaults to post.

Field mapping on the created document:

FieldTypeDescription
titlestringArticle title.
slugslug{ _type: "slug", current: … }.
excerptstringThe meta description.
bodyblock[]Portable Text content.
publishedAtdatetimeSet to now on publish; omitted when as_draft is true.

Contentful setup#

Uses the Content Management API: the entry is created, then published in a second version-locked call. With as_draft: true the entry stays in draft.

  1. In Contentful, open Settings → API keys → Content management tokens and generate a personal access token (CMA token).
  2. In Dashboard → Integrations, enter the space ID, environment (defaults to master), and the token. Optional: a content type ID — defaults to blogPost.
  3. Make sure the target content model has fields for title, slug, body, and description. The defaults map to field IDs title, slug, body, description; a custom field_map on the integration overrides any of them.

The body is written as Markdown into a Long Text field (Contentful renders markdown natively for long text), with the H1 stripped. Locale is en-US for English articles, the raw language code otherwise — make sure that locale exists in your space.

A/B titles#

Every article ships with title_variants — three alternates with different psychological hooks (data-led, how-to, contrarian). The publish call always uses the primary title; the variants are there for your own experimentation layer, ad headlines, or social captions. Rotate them client-side and keep the slug stable.