Skip to content

POST /v1/posts

Create a post on the site that owns the API key. With status: "published" it is published in the same call.

  • URL: https://api.canverly.com/v1/posts
  • Method: POST
  • Auth: Bearer API key — scope posts:write
  • Content-Type: application/json
  • Max body: 4 MiB
  • Rate limit: 60 req/min per key

For worked, multi-language examples see the publishing guide.

Header Required Value
Authorization yes Bearer ck_…
Content-Type yes application/json
Idempotency-Key no Opaque id (≤ 255 chars). A replay with the same key + body returns the original 201 (no duplicate). See Idempotency.
Field Type Required Notes
title string yes Post title. Must be non-empty.
blocks_json object yes Block document: { "v": 1, "blocks": [...] }. See Content blocks.
status string no "draft" (default) or "published".
slug string no Auto-derived from title if omitted.
excerpt string no Stored as the SEO description.
language string no BCP-47; default "pt-BR".
post_type string no "post" (default), "page", or a custom slug from /v1/post-types.
category_slugs string[] no Category slugs (drive category archives).
tag_slugs string[] no Tag slugs.
{
"title": "Hello from the API",
"status": "published",
"slug": "hello-from-the-api",
"excerpt": "A short summary.",
"language": "en",
"category_slugs": ["news"],
"tag_slugs": ["api"],
"blocks_json": {
"v": 1,
"blocks": [
{ "id": "p1", "type": "paragraph", "text": "Body text." }
]
}
}
{
"id": "01K8Z9K3F7T8M2QYV5N6B4WJ8R",
"slug": "hello-from-the-api",
"status": "published",
"public_url": null
}
Field Type Notes
id string Post ULID (Crockford base32).
slug string Final slug (may be normalized from your input).
status string "published" or "draft".
public_url string | null Currently null; build the URL as https://<primary_domain>/<slug> (see /v1/sites/me).

Envelope: { "error": { "code", "message" } }. Full table in Errors & rate limits.

Status error.code When
400 bad_request Malformed JSON, or title empty.
401 unauthorized Missing/invalid/revoked key.
403 forbidden Key lacks posts:write.
413 — Body over 4 MiB.
415 — Missing/incorrect Content-Type.
422 unprocessable Missing blocks_json, wrong field type, or a key created before publishing was enabled.
429 rate_limited Over 60 req/min — honor Retry-After.
5xx upstream Transient; retry with backoff (reuse the same slug).

Send an Idempotency-Key header to make retries safe. The first request runs normally; its 201 response is cached for 24 hours. Any replay with the same key and the same body returns that original response with an extra Idempotency-Replayed: true header — the post is created at most once.

Janela do terminal
curl -X POST https://api.canverly.com/v1/posts \
-H "Authorization: Bearer ck_live_xxx" \
-H "Idempotency-Key: source-item-42" \
-H "Content-Type: application/json" \
-d '{ "title": "…", "blocks_json": { "v": 1, "blocks": [] } }'
  • The key is scoped per api key + request body, so it can’t collide across sites or with a different payload.
  • A request that errors caches nothing — retrying re-runs it.
  • Sending the same key with a different body is treated as a new request (different post).
  • PATCH /v1/posts/{id} — edit or re-date an existing post.
  • GET /v1/posts — read the published archive, with filters, keyset pagination and since for incremental sync.
  • POST /v1/import — create many posts in one NDJSON request instead of a loop.
  • Post SEO — title, description, canonical URL, no_index and schema_type are set on their own route, not in the create body.

There is no public DELETE for posts: deleting content is an admin action.