Adventure Format — Markdown & JSON
The deterministic, no-AI authoring spec for DND.chat adventures — Markdown and JSON rules, scene limits, and copy-paste examples.
When you want the Autopilot DM to run your own adventure — not a published module, not an AI-generated one-shot — you hand DND.chat a structured file. A .md/.markdown or .json adventure is parsed deterministically: no language model, no PDF reader, no page range, and zero cost. Because you supply the content yourself, this is the legally-clean alternative to running a print module through the PDF Shredder.
This page is the canonical authoring spec. It's the same guide DND.chat shows you in-app via the Structured format guide help modal when you stage a file in the Autopilot DM panel.
Why use it
The structured importer turns your text into the same scene graph every other ingest source produces, then saves it. Specifically:
- No AI, no cost. Parsing is purely mechanical — there is no LLM pass, no
pdf.js, and no per-page processing. Import is instant. - Your content stays yours. You're not extracting someone else's PDF; you wrote it.
- It runs like any other adventure. Scenes save to your campaign's chat_spaces.metadata.autopilot with
source: "structured"and are embedded for retrieval, so /recall and the Autopilot DM's per-turn memory work exactly as they do for an imported module.
How import works
Two parsers sit behind one importer. The right one is chosen by your file:
- By extension.
.md/.markdown→ Markdown parser;.json→ JSON parser. - Ambiguous extensions are sniffed: if the trimmed text starts with
{or[, JSON is tried first; otherwise it's parsed as Markdown.
To import, open the Autopilot DM panel in your campaign tavern, click Choose PDF / Markdown / JSON, pick your file, and click Import. The panel detects the structured format and hides the PDF-only page-range and suggested-start controls — those don't apply. Parsing happens immediately with no AI cost, and the scenes save straight to your campaign.
Markdown rules
Markdown is the friendliest way to author. The parser walks your document top to bottom and builds scenes from a small, predictable set of rules:
- A level-1 or level-2 ATX heading (
#or##) opens a new scene, titled by the heading text. Deeper headings (###and below) are treated as ordinary body text, not new scenes. - A blockquote run (
> …) appearing directly under the heading, before any prose becomes that scene's boxed read-aloud — the "read this to the players" text. - A blockquote that appears after prose folds into the body instead of the boxed read-aloud.
- Ordinary paragraphs append to the scene body.
- The first image
under a scene records that scene'smapUrl. Image-only lines contribute nothing else to the body. - Content before the first heading seeds an implicit scene titled Opening, so nothing you write is dropped.
A copy-paste Markdown example
# The Sunken Crypt
> Cold water laps at your ankles as you descend the last step. The
> air smells of brine and old rot. Somewhere ahead, something drags
> itself across stone.
The stair opens into a flooded antechamber. Three sarcophagi line the
far wall, their lids slid half-open. A faint green glow leaks from the
middle one.

## The Drowned Guardian
> The water erupts. A bloated, barnacle-crusted figure rises, seaweed
> trailing from its rusted armor.
The guardian attacks anyone who disturbs the sarcophagi. It fights
until destroyed, then crumbles to silt.Here the lead blockquote under each heading becomes the boxed read-aloud, the following paragraphs become the scene body, and the image is captured as the first scene's mapUrl.
JSON rules
JSON gives you exact control over every field. The shape is validated with zod against the adventure schema, so a malformed file is rejected rather than half-imported.
{
"title": "The Sunken Crypt",
"scenes": [
{
"title": "The Sunken Crypt",
"readAloud": "Cold water laps at your ankles as you descend the last step. The air smells of brine and old rot.",
"npcs": ["Drowned acolyte"],
"exits": ["Collapsed tunnel north", "Flooded stair back up"],
"mapUrl": "https://example.com/crypt-map.png"
},
{
"title": "The Drowned Guardian",
"readAloud": "The water erupts. A bloated, barnacle-crusted figure rises, seaweed trailing from its rusted armor."
}
]
}- The top-level
titleis optional;scenesis required and must contain at least one scene. - Each scene's fields —
title,readAloud,npcs[],exits[],mapUrl— are all optional. - A
```jsoncode fence is stripped before parsing, so you can paste a fenced block straight from a chat or doc and it still imports.
Limits
Both parsers share the same caps. Stay inside them and your file imports cleanly:
| Limit | Cap |
|---|---|
| Scenes per adventure | up to 200 |
| Read-aloud per scene | ≤ 10,000 characters (MAX_READ_ALOUD) |
| NPCs per scene | ≤ 12 |
| Exits per scene | ≤ 8 |
| Minimum scenes | ≥ 1 |
One more thing worth knowing: scenes whose read-aloud is shorter than roughly 80 characters (MIN_SCENE_BODY) can be dropped during import. If a scene matters, give it a sentence or two of substance.
A note on maps
mapUrl is carry-through only. The importer captures it — from a Markdown  image or a JSON mapUrl field — and persists it with the scene, but scene maps are not rendered yet. Maps are also never extracted from PDFs; mapUrl only ever comes from a structured file you author. Treat it as forward-looking: setting it today does no harm and reserves the value for when map display lands.
Related
- DND.chat — The AI-DM Tavern — where you import and run the adventure.
- PDF Shredder — the import path for published modules, when you'd rather not author by hand.
- Slash Commands — including
/recall, which answers from your imported scenes. - How It All Connects — the shared spine behind the DND family.
The AI Features Library
Every AI-powered feature across the DND family — the Autopilot DM, table assists, color and voice, character art, and the combat help that isn't AI at all. What each one does, what triggers it, whether it's live, and what it falls back to.
FAQ & Troubleshooting
Quick answers on accounts, beta pricing, and credits — plus the overloaded-term disambiguations and failure messages that trip people up.