Import From Markdown
Bring a folder of Markdown files into a Dotabase with one command — frontmatter becomes properties, and re-runs stay idempotent.
One command pulls a folder of Markdown files into an existing Dotabase. The CLI walks the folder, and each file lands as a row whose fields come from the file's frontmatter and whose body is the file's Markdown.
The Command
Point import-markdown at a directory and name the target by slug:
dots dotabases import-markdown ./notes --dotabase reading-list
The CLI walks the directory tree recursively, reads every *.md file it finds, and
posts them to the import route. The Dotabase must already exist — see
Create a Dotabase. --dotabase is required and takes a
slug, which the CLI resolves before it sends anything, so a slug typo fails without
touching your data.
What Gets Walked
The walk is recursive and deterministic — directory entries are visited in sorted
order — and the extension filter is exactly .md. A .markdown or .mdx file is not
picked up.
There are no ignore rules on this command. It does not read .gitignore, and it does
not skip node_modules or dotted directories, so pointing it at a repository root
imports far more than you meant. Point it at the folder you actually want, or use
dots dotabases index, which is the command that takes --exclude.
Files travel in batches rather than one request each: five files per request normally,
and thirty when embedding, classification, AI fill, and tag inference are being deferred.
The larger batch is why --defer-enrichment sends a large import in fewer requests.
How A File Maps To A Row
Each file is parsed into YAML frontmatter and a Markdown body. The server-side normalizer maps the frontmatter onto the row: the body becomes the row body, and frontmatter keys become the row's title, tags, domains, DIIICE type, and properties. A missing title falls back to the body's first heading, and then to the filename.
What happens to a key the normalizer does not recognize depends on which normalizer
runs. The default for an ordinary Dotabase maps a known set of keys and drops the
rest. The declarative field_map normalizer instead sweeps every unmapped key into
the row's metadata, where it is preserved verbatim. Choose it with --normalizer when
your frontmatter carries fields you want kept.
Re-running Is Safe
Import is idempotent. Row identity is the file's path relative to the directory you
named, recorded on the row as source_path, so the same file updates the same row on
the next run instead of creating a duplicate. A file that authors a source_path key
in its own frontmatter cannot hijack another row's identity — reserved keys are set
aside rather than honored.
A re-run is a patch, not a replacement. Metadata and properties merge key by key, so a field you added in the app survives; tags are replaced wholesale from the file; and the body is overwritten, because a re-import treats the file as the source of truth. The row keeps its human id, and its edges survive untouched.
Import draws no edges on its own. Every edge-drawing behavior is opt-in, so unless you
have bound one to the Dotabase, connect rows separately with dots edges create once
they are in place.
Useful Flags
--skip-behaviors <list>— suppress named behaviors on the imported rows (for example,classification,tag_inferenceto keep an authored DIIICE type and tags instead of letting inference set them). Names are not checked against the catalog, so a misspelled one is accepted and quietly does nothing.--defer-enrichment— import immediately and enrich embedding, classification, AI fill, and tag inference in the background.--normalizer <key>/--normalizer-config <path>— choose or configure the frontmatter-to-row mapping.
Import Or Index
import-markdown re-sends every file it walks. For an ongoing sync that diffs by
content hash and re-imports only what actually changed, use dots dotabases index
instead — it adds --exclude <glob> for skipping paths and --set <pair> for
stamping a property on every imported row, and an unchanged file costs no request at
all.
Pick one of the two for a given folder and stay with it. The two commands compute
source_path differently — index namespaces it by directory — so running both
against the same folder into the same Dotabase produces two rows per file rather than
converging on one.
Import From Markdown · This page in the graph
- mentionstoDIIICE
- mentionstoEdge
- mentionstoDIIICE
- mentionstoDIIICE: Data
- mentionstoDotabase
- mentionstoBehavior
- mentionstoDOT
- mentionstodots rows, dots edges, and dots pages
- mentionstodots dotabases, dots row-templates, and dots spaces
- referencestoCreate A Dotabase
- mentionsfromYour First Dotabase
- referencesfromCreate A Dotabase
- referencesfromDotabases Overview
- documented byfromConfigurable Markdown Normalization
- documented byfromCLI Markdown Import and Incremental Indexing