Documentation menu

writavo content api

The programmable half of the CMS

The CMS control plane for a Writavo Site.

What this API is for#

Writavo stores your content, serves it to your domain, and can generate it for you. This API is how you drive all three from your own code: create and edit articles, organise them with categories, tags and authors, upload media, publish and schedule, and ask the AI engine to do a piece of work.

It is the same surface the dashboard is built on, so anything the dashboard can do to your content, you can do here. A change made through this API and the same change made by a person in the dashboard are indistinguishable afterwards, including to your webhooks.

The one thing this API deliberately does not cover is account administration. Team members, roles, billing and delivery routing stay in the dashboard, because they are the controls that decide what a key is allowed to do in the first place.

Base URL#

base url
https://api.writavo.com/v1

One hostname, every customer. There is no per-account subdomain and no Site parameter, because the Site is resolved from your key on every request.

How it works#

The Writavo Content API is the programmable half of the CMS. It lets you create, edit, organise, publish and schedule content without opening the dashboard, and it is the same surface the dashboard itself is built on.

The Site is resolved from your key, never from your request

Every key belongs to exactly one Site. The API resolves the owning Site from the key on every request, so there is no site or tenant parameter anywhere in this specification. An object belonging to a different Site is not visible to you at all: it returns 404, never 403, so the API never discloses that it exists.

Draft by default: what happens if you create an article and never publish it

POST /articles always creates the article at status: draft. A draft is private. It is not readable on your public blog, it is not in your sitemap, it is not in any feed, and no part of the AI pipeline will ever pick it up, rewrite it or publish it. It simply sits there, editable, until you call POST /articles/{id}/publish or delete it.

There is no way to create an article that is public in one call. Publishing is always a separate, explicit request. That is the safety property this whole API rests on: nothing you send us becomes public by accident.

Statuses you may set, and statuses you may only read

Three statuses are yours: draft, scheduled and published. You move between them with the lifecycle endpoints (/publish, /unpublish, /schedule, /cancel-schedule), not by PATCHing status.

The remaining ten statuses belong to the AI pipeline. You can read them, so you can see what the engine is doing, but you cannot set them. A PATCH that tries to move an article into a pipeline status is rejected with 422 VALIDATION_FAILED. This prevents an API client from injecting work into the generation engine, and therefore into your bill.

What happens if you run out of credits mid-pipeline

POST /pipeline/runs is the only billable operation in this API. Everything else, including publishing, is free: publishing an article you already have makes no external call.

A pipeline run is gated by four independent checks, in this order:

  1. Scope. Your key must carry pipeline:run, or you get 403 INSUFFICIENT_SCOPE.
  2. Entitlement. Your plan must include the capability, or you get 402 NOT_ENTITLED. Note this applies to the AI pipeline only. The CMS half of this API - content, media, taxonomy, webhooks, reads and writes - is included on every plan, and is billed by usage rather than gated by tier.
  3. Credits. Your organisation must be able to afford the next unit of work, or you get 402 INSUFFICIENT_CREDITS.
  4. Spend cap. Your Site's own monthly cap must not be reached, or you get 402 SPEND_CAP_REACHED.

These are four distinct error codes on purpose, because the fix differs: buy credits, raise your cap, or upgrade your plan.

A run is a request to the engine, not a transaction. If credits run out part way through a run that has already started, the engine stops cleanly at the next stage boundary. Work already completed is kept and already charged. Nothing is rolled back, no article is left half written, and every article stays at whatever status it legitimately reached. The run finishes with status: partial and a reason you can read from GET /pipeline/runs/{id}. Top up and request another run and the engine resumes from where it stopped.

Versioning promise

/v1 is additive only. We may add new endpoints, new optional request fields and new response fields, and you must tolerate unknown response fields. We will not, within /v1, remove a field, remove or narrow an enum value, rename anything, make an optional request field required, or change the HTTP status code of an existing outcome. Any of those would ship as /v2, with /v1 supported alongside it.

New values may be added to read-only enums (for example article.status, if the pipeline grows a stage). Treat every read-only enum as open and fall through gracefully on a value you do not recognise.

Where to start#

The specification#

Everything in the reference is generated from one file, which is served at a stable URL so you can generate your own client rather than hand writing one.

openapi 3.1
https://writavo.com/openapi.yaml
  • OpenAPI 3.1, so most generators consume it directly.
  • Every operation states the scope, the plan feature, the rate limit class and whether a publishable key may reach it.
  • The docs you are reading are built from it. If a page and the spec ever disagreed, the build would fail rather than publish.