---
title: "Call the createDocument MCP tool"
method: POST
path: "/api/v1/actions/createDocument"
tags: ["Actions"]
---

# Call the createDocument MCP tool

`POST /api/v1/actions/createDocument`

## Request

- Base URL: `https://app.reachium.io`
- URL: `https://app.reachium.io/api/v1/actions/createDocument`
- Auth: HTTP bearer

Author a NEW Reachium document from markdown YOU write (you write the content; this only saves it, there is no server-side generation). For a lead-magnet resource, pull getPlaybook("content", "document-anatomy") BEFORE writing: the document type ladder, the conversion anatomy (CTA at top, copy-paste blocks per framework), and the quality checklist live there, and getBrandProfile supplies the real proof points and links to write from. Pass publish:true to make it a public /p/ page immediately, shareable outside Reachium and required before attachResourceToPost will accept it; leave it unset (or false) to save a private draft (updateDocument can revise it later, publish it with publish:true, and take a published page offline again with unpublish:true). A private draft shows up in a later getDocuments call marked status private_draft (the default listing includes your own private drafts). For a lead-magnet resource, publish is NOT required: pass this document_id straight into createLeadMagnetCampaign together with a hook post - that tool does the document/keyword/post binding itself and delivers the resource over a private per-lead link. An unpublished hook (draft, planned, approved, or scheduled) arms when it publishes; an ALREADY-PUBLISHED Reachium post works too and back-enrolls everyone who already commented the keyword once the campaign activates. Alternatively, attachResourceToPost binds a document to an EXISTING post OUTSIDE the campaign flow; unlike createLeadMagnetCampaign it DOES require publish:true (it only accepts public, non-admin-restricted documents), and publishing a post bound only that way does not by itself arm any funnel, since no campaign is linked. Markdown up to 60000 characters. Returns document_id, title, is_public, public_url (null when not published; a workspace with no booking slug gets one auto-generated from its name at first publish, and in the rare case that fails, public_url is null and a warning says the page is unreachable), and ui_url. If publish:true fails, the document is not left as a draft: the whole row is rolled back, so retry by sending content_markdown again. publish:true is two-step: that FIRST call (no confirm_token) returns a preview and a confirm_token and creates NOTHING; call again with the same title and content_markdown plus confirm_token to actually create and publish. Creating a private draft (publish unset or false) stays single-call, no confirm_token needed.

Called through the Actions bridge: the response is that tool's own result, wrapped as `{ data: <tool result> }` under HTTP 200. Billing, daily-cap, and confirm-step outcomes are embedded INSIDE `data`, never remapped to an HTTP status (see the templated `/api/v1/actions/{toolName}` operation above for the full protocol, and `x-mcp-actions` for this tool's machine-readable scope/cap/confirm_behavior fields). Authentication, request-shape, and optional idempotency-protocol failures still use their documented non-200 statuses.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `title` string: Required unless source_asset is given (then the first heading, or the file name, is used). titleFirstLine truncates to 120 chars regardless, so this is the true bound.
  - `content_markdown` string: Required unless source_asset is given. Supported: headings (# through ######), paragraphs, bullet and numbered lists (a blank line between numbered items keeps counting), pipe tables (| a | b | header, | --- | --- | separator, then rows; \| for a literal pipe in a cell), **bold**, *italic*, [text](url) links, `inline code`, fenced ``` code blocks, > blockquotes, --- horizontal rules, - [ ] / - [x] checklists, and images on their own line as ![caption](https://...) (an https URL; inline images inside a sentence are not converted). Highlights and colors: ==text== is a yellow highlight, ==<color>:text== a colored highlight, @@<color>:text@@ colored text, with <color> one of gray, brown, red, orange, yellow, green, blue, purple, pink OR a custom #RRGGBB hex (exactly 6 hex digits, e.g. ==#D1FAE5:win== or @@#B91C1C:warning@@; anything else renders as literal text). Marks must hug their text (==key point==, never == key point ==), and may wrap bold or italic (==**key**== is bold AND highlighted). Layout: end a paragraph, heading, or list line with a space plus {center}, {right}, or {justify} to align it; images take ![caption](url){width=420} (pixels, 50-1200) and/or an alignment token like {width=420 left} (images default to centered). Pull getPlaybook("content", "document-styling") BEFORE styling a document: the discipline (what to highlight, which colors mean what, the readable palette, when to center or size) lives there. NOT supported (renders broken or missing, use plain formatting instead): nested or indented list levels (they all flatten to one level), setext headings (a title on its own line followed by a row of ==== or ----), bold nested inside italic or italic nested inside bold (the ** or * marks stay visible instead of rendering), and per-character font sizes (use heading levels for size hierarchy).
  - `source_asset` string: Build the body from an uploaded file instead of content_markdown: a name from listAssets with kind markdown (used as-is) or docx (converted to Markdown; embedded images are dropped with a warning). pdf and image assets are refused with their hosted URL, link those from a document instead. Upload first with requestUploadUrl + finalizeAsset. Once the document exists the source file is removed from the library: it was a staging file, not a document.
  - `publish` boolean: true to make the document a public /p/ page immediately (required before attachResourceToPost will accept it), which is two-step: the first call previews and returns a confirm_token, and nothing is created until you send it back. NOT required for createLeadMagnetCampaign, which delivers privately per-lead regardless of is_public. Defaults to a private draft, which is created on the first call with no confirmation step.
  - `slug` string: Custom URL slug for the public /p/ page; only valid together with publish:true. Lowercased and hyphenated automatically, capped at 50 characters on a word boundary. For SEO: 3-6 words, the primary keyword FIRST, no filler words or dates (good: "linkedin-outreach-playbook"; bad: "my-2026-guide-v2-final"). Refused if another document already owns it (never silently suffixed). Omit to auto-generate from the title.
  - `folder` string: File the new document into a folder, by folder id or (case-insensitive) name (getDocuments lists them under `folders`). Omit for the root. An unknown name is refused (folder_not_found) and names the existing folders.
  - `confirm_token` string: From the preview response of a publish:true call; resend with the same title, content_markdown, and slug (if any) to execute. Never needed when creating a private draft.

## Response `200`

The result of calling this tool.

- object
  - `data` unknown, required
  - `error` unknown, required

## Other responses

- `400`: The request body was not a JSON object (code invalid_request), or Idempotency-Key is malformed (code invalid_idempotency_key).
- `401`: Missing or invalid API key. Send `Authorization: Bearer rmcp_...` (code unauthorized).
- `403`: This tool is out of the key’s granted scope (code insufficient_scope).
- `409`: This Idempotency-Key has an unresolved in-flight or ambiguous result (code request_in_flight). Reconcile before using a new key.
- `422`: This Idempotency-Key was already used with a different request body (code idempotency_key_reuse).
- `429`: Rate limited: shared `mcp:ip:{ip}` (600/60s) or `mcp:key:{keyId}` (120/60s) bucket, the SAME buckets the MCP endpoint uses (code rate_limited).
- `500`: Unhandled server error (code internal_error).

---

[API](https://skmtc.dev/reachium/apis/reachium-api.md) · [All operations](https://skmtc.dev/reachium/apis/reachium-api/llms.txt) · [OpenAPI document](https://skmtc.dev/reachium/apis/reachium-api/revisions/30c16b30b499?raw)
