Replace a draft body with a structured document
set_draft_bodyReplace a draft's body with a node-by-node Substack document for exact control over captions, buttons, and layout when markdown is insufficient. Validates the document before sending.
Instructions
Replace a draft's body with a Substack document you build node by node. Use this when you need exact control that markdown cannot express, such as a specific image caption or a button.
The document is {"type":"doc","content":[ ... ]}.
Nodes: paragraph; heading (attrs.level 1-6, rendered by Substack as its H1-H3); blockquote; bullet_list / ordered_list (attrs.order, attrs.tight) with list_item children; code_block (attrs.language); horizontal_rule; image2 (attrs.src, attrs.alt, attrs.caption); youtube2 (attrs.videoId); twitter2 (attrs.url); spotify2 (attrs.url); vimeo (attrs.url); paywall (everything after it is for paying subscribers); button (attrs.url, attrs.text); subscribeWidget (attrs.text); footnote (attrs.number) with footnoteAnchor.
Marks on text nodes: strong; em; code; strikethrough; link (attrs.href).
The document is validated before it is sent, so an unknown node name is an error here rather than a post that renders wrong on Substack. For ordinary writing, update_draft with markdown is easier and produces the same result.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Draft id. | |
| document | Yes | The full ProseMirror document as a JSON string, shaped {"type":"doc","content":[...]}. | |
| publication | No | Which connected publication to act on, matched loosely against its hostname (for example 'example.substack.com' or just 'example'). Defaults to the first connected publication. |