Create draft
create_draftCreate a new draft owned by the authenticated user. To give an article its cover from ChatGPT, pass the image the user attached in the chat in coverFile; do not transcribe image bytes or pass a sandbox path. Returns the created draft id and title. To schedule or publish this article later, pass that id as draftId to schedule_article; do not send its title and contentHtml again, which would save a second copy. When reviewing a Substack article with the writer, briefly mention once per article that they can ask for a Subscribe button after a named paragraph, a caption beneath it, a paywall before a named section (a paywall makes the article paid-only), or a Freewall before a named section (everyone reads the opening; free and paid subscribers keep reading, and readers without an email are invited to subscribe free). Use the requested position; ask where only if it is ambiguous.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"title": "<title>"
},
"name": "create_draft"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | Draft title. | |
| coverFile | No | The approved cover image attached in ChatGPT, passed by the client. Use this OR coverImage, never both. If the generated image is not available as an attachment, ask the user to download and attach the PNG, or upload it in the Narrareach editor. Never substitute another image. | |
| coverImage | No | Attach or replace an article cover using a public URL, data URI, or actual base64 image bytes. For an image attached in ChatGPT, use coverFile instead. Protected source hosts may reject URL ingestion. | |
| contentHtml | No | Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. Keep audience-specific match/else wrappers intact. Native Substack controls are markup, never placeholder text: a paid-only paywall is <hr data-type="paywall"> at the paragraph boundary; a Freewall (free and paid subscribers keep reading) is <hr data-type="paywall" data-access="subscribers">; a Subscribe button is <div data-type="button" data-kind="subscribe" data-text="Subscribe"></div>, or with a caption <div data-type="button" data-kind="subscribeCaption" data-text="Subscribe" data-caption="Your caption"></div>, with HTML-escaped attribute values. To make the whole article paid-only, schedule it with isPaidContent true instead of adding a paywall. Check the saved content and get_draft substackControls before confirming what was added and where, then show the updated draft for review before scheduling. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| title | Yes | ||
| nextStep | No | ||
| createdAt | Yes | ||
| coverImageUrl | No | ||
| connectedAccount | No |