Guide

AI blog writer for Strapi: fact-checked drafts in your CMS

uprank is an AI blog writer that sends its articles to Strapi 5 as drafts. It researches and drafts each article, checks it against 13 hard quality gates, and, when you press Create / update draft on a version that passed them, creates the entry in your own content type through Strapi’s REST API, with each article field mapped to the attribute names you choose. The push creates a draft only, so a person reviews the article in Strapi before anything is live.

Updated

How it works

Only the Publishing tab sends anything to Strapi.

StageWhat uprank doesStrapi REST call
ConnectA workspace owner saves your Strapi base URL and an API token. The token is encrypted at rest and sent as a Bearer token. Validate sends one authenticated request and marks the connection connected or invalid.GET /content-type-builder/content-types
MapA publish target stores your content-type UID and a mapping from each article field to your own Strapi attribute name. No content type is assumed.None. The mapping is stored in uprank.
Writeuprank researches, drafts and scores the article in the app, or from your coding agent over MCP.None. Nothing is sent to Strapi yet.
Create draftCreate / update draft on the article’s Publishing tab first checks that the version’s current quality assessment passed every hard gate, then records the publication, then creates the entry with status draft. The request carries an idempotency key.POST /api/{route}?status=draft
Update draftPressing the button again on a publication that already has a documentId updates that entry instead of creating another.PUT /api/{route}/{documentId}?status=draft
RecoverIf Strapi rejects the payload, the publication is marked FAILED and shows the error. Fix the content type in Strapi, then press Retry; the troubleshooting table below maps each error to its likely cause.The same POST or PUT, sent again.
Go liveNot sent by the Publishing tab, which creates and updates drafts only. You review the draft in Strapi.None.

Connect Strapi

6 steps, from an empty content type to a draft in Strapi.

  1. Step 1

    Create the content type in Strapi

    uprank does not create content types or fields. In Strapi, make a collection type with attributes for the title, slug and body, plus any optional fields from the mapping table below.

  2. Step 2

    Create a Strapi API token

    Create an API token in Strapi that is allowed to create and update entries in that content type. uprank sends it as a Bearer token, and it uses POST and PUT requests.

  3. Step 3

    Add the connection in uprank

    Open Connections, choose New connection, keep the type as STRAPI, and enter your Strapi base URL and the token. Only a workspace owner can add a connection. The token is encrypted at rest and never shown again.

  4. Step 4

    Validate the connection

    Press Validate on the connection. uprank sends one authenticated request to your Strapi and records the connection as connected or invalid, with the time it was checked.

  5. Step 5

    Create a publish target and map the fields

    Choose New publish target, pick the connection, and enter the content-type UID, for example api::article.article. uprank turns the UID into the REST route by pluralizing its name; to use a different route, enter the plural API route instead of the UID. The dialog starts with two example rows: replace them so the left side is a key from the mapping table below and the right side is your Strapi attribute name.

  6. Step 6

    Push a draft from an article

    Open the article, choose the Publishing tab, pick the target and press Create / update draft. The button is enabled once the version’s current quality assessment has passed every hard gate; until then the tab says why. The entry appears in Strapi as a draft, and the tab shows its documentId and the publication’s status history.

Field mapping

Each key maps to one of your Strapi attributes. Map only the keys your content type has.

KeyWhat it sendsFilled by the Publishing tab
titleThe article title.Yes
slugThe article slug.Yes
bodyThe body as a Markdown string, the same as bodyMarkdown.Yes
bodyMarkdownThe body as a Markdown string.Yes
bodyHtmlThe body as an HTML string.Yes
bodyBlocksThe body as Strapi blocks JSON, for a Blocks field. Needs the article’s structured body.Yes
excerptThe version’s excerpt, or one computed from the body when it has none.Yes
seoTitleThe version’s SEO title, when it has one.Yes
seoDescriptionThe version’s meta description, when it has one.Yes
featuredImageAccepted in a mapping, but the Publishing tab sends no value for it.No
authorAccepted in a mapping, but the Publishing tab sends no value for it.No
categoriesAccepted in a mapping, but the Publishing tab sends no value for it.No
tagsAccepted in a mapping, but the Publishing tab sends no value for it.No
publicationDateAccepted in a mapping, but the Publishing tab sends no value for it.No
canonicalUrlThe site’s base URL followed by the slug.Yes
citationsAccepted in a mapping, but the Publishing tab sends no value for it.No

The key on the left must be one of these. A mapping with any other key, or an empty mapping, fails with a mapping error before anything is sent to Strapi. A key whose value the Publishing tab does not fill is left out of the request.

Body formats

The body format follows which key you map. Map one body key, or several to different attributes.

bodyMarkdown sends a Markdown string
For a text field. body does the same.
bodyHtml sends a HTML string
For a text field that holds HTML.
bodyBlocks sends a Strapi blocks JSON
For a Blocks field. Paragraphs, headings, lists, quotes and code map directly. Tables, callouts, images and citations become paragraphs or links, so no content is dropped.

Worked example

A collection type with the UID api::article.article, a title, a slug, an excerpt and a Blocks body, mapped to the matching uprank keys. The Strapi schema is yours to build in Strapi; the request below is what uprank sends for a fixed sample article.

Your Strapi content type (api::article.article)

{
  "kind": "collectionType",
  "collectionName": "articles",
  "info": {
    "singularName": "article",
    "pluralName": "articles",
    "displayName": "Article"
  },
  "options": {
    "draftAndPublish": true
  },
  "attributes": {
    "title": {
      "type": "string",
      "required": true
    },
    "slug": {
      "type": "uid",
      "targetField": "title"
    },
    "excerpt": {
      "type": "text"
    },
    "body": {
      "type": "blocks"
    }
  }
}

Mapping on the publish target

uprank keyStrapi attribute
titletitle
slugslug
excerptexcerpt
bodyBlocksbody

The request for a sample article: POST /api/articles?status=draft

{
  "data": {
    "title": "Reversible decisions",
    "slug": "reversible-decisions",
    "excerpt": "How to tell a reversible decision from a one-way one.",
    "body": [
      {
        "type": "heading",
        "level": 2,
        "children": [
          {
            "type": "text",
            "text": "Why a two-way door is cheap"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "text": "A reversible decision can be undone at a small cost."
          }
        ]
      },
      {
        "type": "list",
        "format": "unordered",
        "children": [
          {
            "type": "list-item",
            "children": [
              {
                "type": "text",
                "text": "Decide quickly"
              }
            ]
          },
          {
            "type": "list-item",
            "children": [
              {
                "type": "text",
                "text": "Review after a week"
              }
            ]
          }
        ]
      }
    ]
  }
}
  • The route comes from the UID: the name after the dot, pluralized, so api::article.article is /api/articles.
  • Only the keys in the mapping are sent. This mapping has no seoTitle, seoDescription or canonicalUrl row, so none of them is in the body.
  • The request also carries the token as an Authorization Bearer header, a JSON content type, and an idempotency-key header set to the publication’s request hash.
  • bodyBlocks became Strapi blocks JSON: the heading, the paragraph and the list are blocks in the body attribute.
  • The article is a fixed sample. The body above is produced by the same field builder and request builder the Publishing tab uses.

What it won’t do

Limits of the Strapi push, as built today.

  • Push live from the Publishing tab. It creates and updates drafts, and you review them in Strapi.
  • Push a version that has not passed its hard quality gates. The Publishing tab refuses a version without a current, passing quality assessment, and so do approving it and, over MCP, publication_publish.
  • Create or change your content types or fields. The attributes you map to must already exist, or Strapi rejects the payload and the publication shows its error.
  • Upload images or fill the featured image, author, categories, tags, publication date or citations fields from the Publishing tab.
  • Retry a failed push on its own. The publication shows FAILED with the error and a Retry button.
  • Publish to any CMS other than Strapi. The Strapi publisher is the only one registered.

Troubleshooting

What the Publishing tab and the Connections page show, the likely cause, and what to do.

What you seeLikely causeWhat to do
Create / update draft is disabled, with a line under it that starts “Score this version first” or “Blocked by”.The version has no current quality assessment, or it fails a hard gate. Nothing has been sent to Strapi.Score the version, or fix the blockers listed on the Quality tab and score it again.
“No publish targets configured. Add one on the Connections page first.”The workspace has no publish target yet, so there is nothing to push to.On the Connections page, add a publish target: a connection, the content-type UID and the field mapping.
Validate marks the connection INVALID, with “Authentication failed: the API token was rejected.” or “Unexpected status …” in the toast.Validate sends one GET to /content-type-builder/content-types. A 401 means Strapi rejected the token; any other status except success or 403 also marks the connection INVALID.Check the Strapi base URL and the token, then press Validate again.
The connection shows CONNECTED, but pushing a draft fails.Validate only reads the Content-Type Builder, and a 403 there still counts as connected (token valid, scope limited). It does not test creating or updating entries.Make sure the token can create and update entries in your content type, then push again; the push result is the real check.
The publication is FAILED with “Could not reach the Strapi server.” The same text is the Validate toast when the check cannot connect.The request threw before any response came back. uprank maps every such failure, including the request timing out, to this message.Check the base URL on the Connections page, and that your Strapi is reachable from uprank’s server and not only from your own network. Then press Retry.
The publication is FAILED with “Field mapping is empty; nothing to publish.” or “Field mapping references unknown canonical fields.”The publish target’s mapping has no rows, or a key on its left side is not in the field table above. This is checked before any request is sent.The app has no edit for a publish target (connection-actions.ts only creates them). Create a new target whose mapping keys come from the field table, and push with it from the Publishing tab.
The publication is FAILED with Strapi’s own message, or “Strapi rejected the payload.”Strapi answered HTTP 400: it refused the data uprank sent. Typical reasons are an attribute in the mapping that the content type does not have, a required attribute that is not mapped, or a value of the wrong type, such as bodyBlocks mapped to a text attribute.Compare the attribute Strapi names with your mapping and with the content type, and match each key to its attribute type (the body formats above). Fix the content type in Strapi and press Retry. If the mapping is wrong, create a new publish target.
The publication is FAILED with Strapi’s message or “Strapi request failed during createDraft.” (updateDraft when it was an update).Any other failing status. A push does not tell a 401, a 403, a 404 or a server error apart, and the tab shows Strapi’s message, not the status code. The usual suspects are a token that is wrong or cannot create and update entries in this content type, and a route that does not exist because the UID or plural route is wrong.In Strapi, check that the token allows create and update on this content type, and that the route exists. uprank takes it from the UID (the name after the dot, pluralized), or uses the plural API route you entered instead of the UID. Fix it, then press Retry. A server error is marked retryable but is never retried on its own.
The publication is FAILED with “The CMS rejected the draft.”Strapi answered with a success status, but the response had no data.documentId, so uprank cannot record a draft. The publisher expects the Strapi 5 response shape.Check that the base URL points at a Strapi 5 server and not at a proxy or another service in front of it.

Drive it from your coding agent

A coding agent can run the same pipeline and gates over MCP. publication_create_draft records the draft publication for a target; pushing it to Strapi is done from the article’s Publishing tab, once the version passes its hard gates.

article_score
Score a version on the quality dimensions and hard gates.
publication_create_draft
Record a draft publication for a publish target in uprank; idempotent and safe to retry. Nothing is pushed to the CMS: the Strapi draft is pushed from the article’s Publishing tab, once the version passes its hard gates.
publication_status
Read the state of a publication.
publication_publish
Record a live publication request for a version with a current, passing quality assessment; needs publication:live and confirm "PUBLISH LIVE". Nothing is pushed to the CMS.

Agent setup guides: connect any MCP-capable coding agent to uprank.

Common questions

Does it work with Strapi 5?
It is built for Strapi 5. Entries are addressed by documentId, and draft or published is set with the status query parameter on the REST API. Strapi 4 is not targeted.
Do I need a particular content type?
No. You use your own content type and map each article field to your own attribute names, so no universal schema is assumed. The attributes you map to must exist in Strapi.
Will it publish articles live in Strapi?
Not from the Publishing tab. It creates and updates drafts, and you publish from Strapi after review. Over MCP, a live publication record needs a credential with publication:live, the literal confirmation PUBLISH LIVE and a passing quality assessment, and every live publish is audited.
What if I press Create draft twice?
You do not get two entries. uprank records the publication before it calls Strapi, keyed to the article version, the target and the mode, and sends that key as an idempotency header. The second press updates the same Strapi entry by its documentId.
What happens when Strapi rejects the article?
The publication is marked FAILED and shows the error. Nothing is retried on its own. Fix the content type in Strapi, then press Retry on the publication to send it again. The app has no edit for a publish target, so a wrong mapping is fixed by creating a new target. The Troubleshooting section of this page maps each error to its likely cause.
Are articles checked before they reach Strapi?
Each article is scored in uprank against 13 hard gates: 6 minimum scores and 7 integrity checks. The Publishing tab only pushes a version whose current assessment passed every hard gate; a version that fails one is refused and nothing is sent to Strapi. A version that passes still lands in Strapi as a draft for review.

Keep reading

Create your workspace