content lifecycle
Nothing becomes public by accident
POST /articles always creates a draft. Publishing is always a separate, explicit request. That is the safety property this whole API rests on.
Draft by default#
There is no request field that can create a public article in one call. Supplying status on a create is a validation error rather than a silent ignore, so a client written against a different CMS fails loudly instead of quietly leaving your content unpublished.
A draft is genuinely private. It is not on your blog, not in your sitemap, not in any feed, and no part of the AI pipeline will pick it up, rewrite it or publish it. It sits there, editable, until you publish it or delete it.
Statuses you may set, and statuses you may only read#
Yours
draftscheduledpublishedMoved with the lifecycle endpoints, never by PATCHing a field.
The engine's
discoveredscoredskippedscrapedgeneratedneeds_improvementneeds_imagesqueuedrejectedfailedReadable, so you can see what the engine is doing. Not settable.
The article state machine. Thirteen values, in two groups.
Yours, settable through the lifecycle endpoints:
draft- private and editable. Where every article starts. Never touched by the AI.scheduled- has a futurescheduled_publish_at. A cron publishes it at that time.published- public on your blog.
The pipeline's, readable but not settable. A PATCH naming any of these is rejected with 422 VALIDATION_FAILED:
discovered,scored,skipped- a candidate source found and triaged.scraped- source fetched, research signal extracted.generated- a draft the engine wrote, awaiting quality review.needs_improvement- failed quality review; queued for a rewrite.needs_images- text is final, images are being generated.queued- finished and waiting for its publishing slot.rejected,failed- abandoned, or errored past retry.
More pipeline values may be added within v1 as the engine grows. Handle unknown values gracefully.
Why the engine's statuses are read only#
PATCH that tries to move an article into a pipeline status is rejected with 422 VALIDATION_FAILED. This is what stops an API client injecting work into the generation engine, and therefore into your bill.Why you cannot PATCH a status
returns 422curl -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, all on the articles resource. Each is safe to repeat: calling one on an article that is already in the target state is a no-op that returns the current state.
POST /articles/{id}/publishputs it on the web now. It needs a non empty title, slug and content, and tells you which is missing.POST /articles/{id}/unpublishtakes it back down and returns it todraft. Nothing is deleted.POST /articles/{id}/schedulesets a future time. A cron publishes it then, whether or not the AI engine is switched on for your Site.POST /articles/{id}/cancel-schedulereturns it todraftand clears the time.
Take a post off the web
returns 200curl -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 200curl -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 200curl -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 when an article first becomes public and then left alone. Unpublishing does not clear it and republishing does not move it, because it is your blog's ordering key: a post that goes back up keeps its place in the archive rather than jumping to the top of the feed.
If you want a post to be new again, that is a new article. If you want to correct the date, that is a dashboard action, deliberately: silently rewriting publication dates is a thing search engines notice.
Delete or unpublish#
Deleting is permanent. There is no trash and no undo in /v1. If you only want a post off the web, unpublish it: everything is kept and the operation is fully reversible.
Both make the URL start returning 404 on your blog once the cache is purged, which happens as part of the request.