Subnano Docs API Access Settings

Posts API

Use these endpoints to manage your own drafts and published posts.

Endpoints

  • POST /api/v1/posts create draft
  • GET /api/v1/posts list posts (?status=draft|published optional)
  • GET /api/v1/posts/:id get post by UUID
  • PATCH /api/v1/posts/:id update post fields
  • DELETE /api/v1/posts/:id delete draft

Create draft

POST /api/v1/posts

Required body fields:

  • paidContentMarkdown or freeContentMarkdown

Common optional fields:

  • title, description
  • freeContentMarkdown
  • enablePaywall, priceRaw
  • primaryCategoryId, secondaryCategoryId
  • slug, canonicalUrl, language, commentsEnabled
  • ogTitle, ogDescription, ogImage
  • creationMethod, creationDetails, creationAttested

Behavior:

  • If enablePaywall is true but paidContentMarkdown is omitted or empty, enablePaywall is treated as false.
  • When enablePaywall is false, either paidContentMarkdown or freeContentMarkdown can supply the freely accessible content.
  • priceRaw is only meaningful when the effective paywall is enabled.
  • If enablePaywall is false or is auto-disabled because paidContentMarkdown is empty, priceRaw is ignored and stored as null.
  • Drafts may be saved without creation details, but free and paid posts cannot be published until creationMethod and creationAttested: true are set.
  • creationDetails is optional plain text with a maximum length of 280 characters.

Supported creationMethod values:

  • human_written
  • ai_assisted_editing
  • ai_assisted_research
  • ai_assisted_drafting
  • primarily_ai_generated

These fields record the author's declaration about the production process. They are not AI-detection results or verified-authorship claims.

creationAttested: true means the author declares:

I have reviewed this post and take responsibility for publishing it.

API clients must not set the attestation automatically. Set it only after the author has reviewed the final post and authorized publication.

Free post example

{
  "title": "Free API post",
  "description": "A free post created through the Publishing API.",
  "freeContentMarkdown": "# Public article\n\nThis entire post is free to read.",
  "enablePaywall": false,
  "primaryCategoryId": 4,
  "language": "en",
  "creationMethod": "ai_assisted_research",
  "creationDetails": "AI helped locate sources; the author reviewed and wrote the post.",
  "creationAttested": true
}

List posts

GET /api/v1/posts?status=draft&page=1&per_page=20

Returns:

{
  "data": [{ "id": "uuid", "status": "draft" }],
  "total": 1,
  "page": 1,
  "perPage": 20
}

Get post

GET /api/v1/posts/:id

Returns a full resource including:

  • paidContentMarkdown
  • freeContentMarkdown
  • ogImageEffective
  • creationMethod, creationDetails, creationAttested

Empty markdown fields are returned as null, not editor placeholder markup.

ogImageEffective follows this order:

  1. Explicit metadata.og.image
  2. First image found in freeContentMarkdown

Update post

PATCH /api/v1/posts/:id

Partial updates are supported. Slug updates are rejected when the post is already published.

Delete post

DELETE /api/v1/posts/:id

Rules:

  • Only drafts can be deleted.
  • Published posts must be unpublished first.
  • Posts with paid purchases cannot be deleted.

Integration Recipe: Post with 2 Images

Use this when you want one clear, copy-safe flow from draft to published.

  1. Create draft
  2. Upload image 1 (intent=content)
  3. Upload image 2 (intent=content)
  4. Patch markdown with both image URLs
  5. Publish with Idempotency-Key

Step 1: create draft

curl -X POST "https://subnano.me/api/v1/posts" \
  -H "Authorization: Bearer $SUBNANO_PUBLISH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My post with two images",
    "description": "Example flow",
    "paidContentMarkdown": "Draft body placeholder",
    "enablePaywall": true,
    "priceRaw": "100000000000000000000000000000",
    "primaryCategoryId": 4,
    "language": "en",
    "creationMethod": "human_written",
    "creationAttested": true
  }'

Save the returned id as POST_ID.

Step 2: upload image 1

curl -X POST "https://subnano.me/api/v1/posts/$POST_ID/images?intent=content" \
  -H "Authorization: Bearer $SUBNANO_PUBLISH_KEY" \
  -F "file=@/path/to/image-1.jpg"

Save returned url as IMG_1.

Step 3: upload image 2

curl -X POST "https://subnano.me/api/v1/posts/$POST_ID/images?intent=content" \
  -H "Authorization: Bearer $SUBNANO_PUBLISH_KEY" \
  -F "file=@/path/to/image-2.jpg"

Save returned url as IMG_2.

Step 4: patch markdown with both image URLs

curl -X PATCH "https://subnano.me/api/v1/posts/$POST_ID" \
  -H "Authorization: Bearer $SUBNANO_PUBLISH_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"paidContentMarkdown\": \"# Final title\n\nIntro text.\n\n![Image 1]($IMG_1)\n\nMore text.\n\n![Image 2]($IMG_2)\"
  }"

Tip: if building payloads in shell, use \n (not \\n) for markdown line breaks.

Step 5: publish

curl -X POST "https://subnano.me/api/v1/posts/$POST_ID/publish" \
  -H "Authorization: Bearer $SUBNANO_PUBLISH_KEY" \
  -H "Idempotency-Key: 6d65f0f8-6897-4d8e-a3cb-f6f12f947f99"

Optional: if you want a dedicated social preview image, upload with intent=og (or set ogImage via PATCH).