Posts API
Use these endpoints to manage your own drafts and published posts.
Endpoints
POST /api/v1/postscreate draftGET /api/v1/postslist posts (?status=draft|publishedoptional)GET /api/v1/posts/:idget post by UUIDPATCH /api/v1/posts/:idupdate post fieldsDELETE /api/v1/posts/:iddelete draft
Create draft
POST /api/v1/posts
Required body fields:
paidContentMarkdownorfreeContentMarkdown
Common optional fields:
title,descriptionfreeContentMarkdownenablePaywall,priceRawprimaryCategoryId,secondaryCategoryIdslug,canonicalUrl,language,commentsEnabledogTitle,ogDescription,ogImagecreationMethod,creationDetails,creationAttested
Behavior:
- If
enablePaywallistruebutpaidContentMarkdownis omitted or empty,enablePaywallis treated asfalse. - When
enablePaywallisfalse, eitherpaidContentMarkdownorfreeContentMarkdowncan supply the freely accessible content. priceRawis only meaningful when the effective paywall is enabled.- If
enablePaywallisfalseor is auto-disabled becausepaidContentMarkdownis empty,priceRawis ignored and stored asnull. - Drafts may be saved without creation details, but free and paid posts cannot be published until
creationMethodandcreationAttested: trueare set. creationDetailsis optional plain text with a maximum length of 280 characters.
Supported creationMethod values:
human_writtenai_assisted_editingai_assisted_researchai_assisted_draftingprimarily_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:
paidContentMarkdownfreeContentMarkdownogImageEffectivecreationMethod,creationDetails,creationAttested
Empty markdown fields are returned as null, not editor placeholder markup.
ogImageEffective follows this order:
- Explicit
metadata.og.image - 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.
- Create draft
- Upload image 1 (
intent=content) - Upload image 2 (
intent=content) - Patch markdown with both image URLs
- 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\n\nMore text.\n\n\"
}"
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).