Import, don't paste
Why the server is the only Markdown renderer, why a published story is a server-rendered page Medium imports faithfully, and why images must be hosted.
Scripto’s defining promise is what you write is what imports. This page explains the machinery behind it and why “import, don’t paste” is the right mental model.
The server is the only renderer
There is exactly one place in Scripto that turns Markdown into HTML: the server. The CLI sends Markdown; the agent never renders. The web canvas produces semantic HTML through TipTap; the server reconciles it back into sections. Because rendering is centralized, the HTML that ships is always the HTML the server produced — there’s no second renderer to disagree with the first.
This is the structural reason formatting “just works”. You’re never translating between two renderers (your editor’s and the destination’s). You author, the server renders once, and that single rendering is what everyone reads.
Publishing yields a page, not a blob
scripto articles publish <id> doesn’t hand you HTML to copy. It assigns a slug and exposes a server-rendered page at /story/<slug>. That page is real HTML served from the database — no JavaScript required to read it, because Medium’s import bot runs none.
Medium imports the page
Medium’s “Import a story” takes a URL. It fetches the page server-side and rebuilds the story from the HTML it finds — headings, code blocks, lists, blockquotes, the centered · · · divider, and figures, in order. The published page is shaped to match what Medium’s importer expects, so the imported story matches what you wrote.
Publish
scripto articles publish <id> → https://scripto.codika.io/story/<slug>.
Import
In Medium: avatar → Stories → Import a story, paste the URL, import.
Images must be hosted
The one rule that follows from “Medium fetches it server-side”: every image must be a fetchable public URL. Medium’s bot runs no JavaScript and can’t read inline data: images. Scripto hosts images for you:
- In the web canvas, dropping an image uploads it to Vercel Blob and inserts the public URL.
- Programmatically,
POST /api/uploadhosts an image and returns its URL (see POST /api/upload).
If a story imports into Medium with missing images, the cause is almost always an inline or non-public image source. Host the image first, then reference its public URL.
The subtitle (dek)
The article’s subtitle is rendered as the leading line under the title, and Medium’s importer reads it as the article subtitle. Set it with scripto articles update (via --file whose body leads with it) or PATCH /api/articles/[id] with { "subtitle": "…" }.
Next
- Import from Medium — the reverse direction: bringing your Medium history in.
- Articles — the fields, including
subtitleandslug. - POST …/publish — the publish endpoint.