---
title: "Nothing becomes public by accident"
description: "Draft, scheduled, published, and the ten statuses that belong to the engine."
canonical: "https://writavo.com/docs/content-lifecycle"
last-updated: "2026-09-23"
---

# Nothing becomes public by accident

`POST /articles` always creates a draft. Publishing is always a separate request.

## Draft by default

No request can create a public article in one call. Sending `status` on a create is a validation error, so it fails loudly instead of being ignored.

A draft is private: not on your blog, in your sitemap or in any feed, and the AI pipeline never picks it up, rewrites it or publishes it. It stays until you publish or delete it.

## Statuses you may set, and statuses you may only read

Thirteen values in two groups.

**Yours, set through the lifecycle endpoints:** `draft` (private; every article starts here and
the AI never touches it), `scheduled` (a cron publishes it at `scheduled_publish_at`) and
`published` (public on your blog).

**The pipeline's, read only** (a `PATCH` naming one is `422 VALIDATION_FAILED`):

- `discovered`, `scored`, `skipped` - a source found and triaged.
- `scraped` - source fetched.
- `generated` - awaiting quality review.
- `needs_improvement` - failed review, queued for a rewrite.
- `needs_images` - images in progress.
- `queued` - waiting for its publishing slot.
- `rejected`, `failed` - abandoned, or errored past retry.

More pipeline values may appear within v1; handle unknown ones gracefully.

You move your three statuses with the lifecycle endpoints below, never by PATCHing a field.

## Why the engine's statuses are read only

> Moving an article into a pipeline status with `PATCH` returns `422 VALIDATION_FAILED`. This stops an API client injecting work into the engine, and into your bill.

**Why you cannot PATCH a status** (returns 422)

```bash

curl -X PATCH https://api.writavo.com/v1/articles/3f1b0c7a-0000-4000-8000-000000000001 \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "queued"
}'

```

The ten pipeline statuses are read only. This is what stops an API client injecting work into the generation engine, and therefore into your bill.

## Moving between states

Four endpoints on the [articles resource](/docs/api/articles). Each is safe to repeat: if the article is already in the target state, it returns the current state and changes nothing.

- `POST /articles/{id}/publish` puts it on the web now. It needs a non empty title, slug and content, and tells you which is missing.
- `POST /articles/{id}/unpublish` takes it down and returns it to `draft`. Nothing is deleted.
- `POST /articles/{id}/schedule` sets a future time. A cron publishes it then, whether or not the AI engine is on for your Site.
- `POST /articles/{id}/cancel-schedule` returns it to `draft` and clears the time.

**Take a post off the web** (returns 200)

```bash

curl -X POST https://api.writavo.com/v1/articles/3f1b0c7a-0000-4000-8000-000000000001/unpublish \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000"

```

Reversible, and nothing is deleted. `published_at` is left intact so a post that goes back up keeps its place in the archive.

**Schedule a publish** (returns 200)

```bash

curl -X POST https://api.writavo.com/v1/articles/3f1b0c7a-0000-4000-8000-000000000001/schedule \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000" \
  -H "Content-Type: application/json" \
  -d '{
  "scheduled_publish_at": "2026-12-01T09:00:00Z"
}'

```

The time must be in the future, and the title, slug and content are checked now rather than at the scheduled moment.

**Cancel a scheduled publish** (returns 200)

```bash

curl -X POST https://api.writavo.com/v1/articles/3f1b0c7a-0000-4000-8000-000000000001/cancel-schedule \
  -H "Authorization: Bearer wv_sk_EXAMPLE0000000000000000000000000000"

```

## published_at is the first publication date

It is set the first time an article goes public and never moves. Unpublishing does not clear it and republishing does not change it, so a post that goes back up keeps its place in the archive.

- Want a post to be new again? Create a new article.
- Need to correct the date? Do it in the dashboard. Search engines notice silently rewritten dates.
- Importing from another system? The first publish may carry the original `published_at` (and `content_updated_at`), as long as both are in the past. After that the date is fixed. See [migrating from another CMS](/docs/migrate).

## Delete or unpublish

- **Deleting is permanent.** There is no trash and no undo in `/v1`.
- **Unpublishing is reversible.** Everything is kept. Use it if you only want a post off the web.
- Both make the URL return 404 on your blog once the cache is purged, which happens as part of the request.
