Docs as code
Project layout
A docs project is a folder of Markdown, one .askvoro file, and a section.json
wherever you want a section.
docs/
├── .askvoro identity — which workspace and knowledge base. init writes it.
├── site.json header and footer links. Optional.
├── 01-welcome.md
├── getting-started/
│ ├── section.json
│ ├── 01-install.md → /~/<workspace>/<knowledge-base>/install
│ └── 02-first-steps.md → /~/<workspace>/<knowledge-base>/first-steps
├── billing/
│ ├── section.json
│ └── invoices.md
└── faqs/
├── 01-do-you-ship-internationally.md
└── 02-how-do-i-cancel.mdTwo files carry configuration, and they are separate on purpose. .askvoro is
identity — which workspace, which knowledge base, which source — generated
once by askvoro init and rarely touched. site.json is presentation, and
you edit it whenever your marketing site grows a page. See
Site navigation.
Titles come from the first heading#
The first # heading in a file becomes the article title and is removed from
the body — so it isn't rendered twice.
# How to cancel your subscription
Open Account → Subscription and choose Cancel.That becomes an article titled "How to cancel your subscription" whose body starts at "Open Account".
Without an # heading, the filename becomes the title. Rely on the heading; it
reads better and survives a rename.
Number your files to order them#
Files sort lexicographically, and that becomes article order. Numeric prefixes give you explicit control:
01-install.md
02-first-steps.md
03-troubleshooting.mdThe prefix is only for sorting. It never appears in the title, and it never
appears in the URL — 01-install.md publishes at /install. Renumbering a
file to reorder it does not move its page.
Use 01, not 1. Ten files sorted as strings put 10- between 1- and 2-.
Prefixes are optional: an unprefixed file sorts alphabetically among its siblings, so a folder can mix both.
Choosing a URL#
The URL segment comes from the filename with the ordering prefix stripped. When
you want something different, set slug: in frontmatter:
---
slug: install
---
# Install the widget on your siteThat publishes at /install instead of /install-the-widget-on-your-site.
Unlike the derived slug — which is computed once, when the article is first
created, so a published page never moves on its own — slug: is applied on
every push. It is how you deliberately rename a live page.
You need it in one other case: a four-digit prefix is left alone so
2026-pricing-change.md keeps its year, but three digits are treated as an
ordinal, so 404-page.md would publish at /page. Set slug: 404-page to
keep it.
If two files would land on the same URL — getting-started/01-overview.md and
billing/02-overview.md both want /overview — the push fails and names both
files. Give one a slug:.
Sections#
Each directory can hold a section.json:
{
"title": "Getting started",
"position": 10
}Markdown inherits the nearest section.json above it. A file with none is
pushed as uncategorized.
Leave gaps in position — 10, 20, 30 — so you can insert a section later
without renumbering the rest.
FAQs#
With a faqs block in .askvoro, Markdown under that directory becomes FAQs
instead of articles: the first # heading is the question, the rest is the
answer.
# Do you ship internationally?
Yes, to most countries. Shipping is calculated at checkout.Filename order becomes FAQ order inside this knowledge base, written in the same transaction as the articles. The dashboard edits those same rows.
Conventions worth adopting#
One question per file, matching the article advice in the help center guide.
Filenames that match the title. 02-how-do-i-cancel.md for "How do I
cancel?" makes the folder browsable without opening anything.
Reordering is safe. Files are matched to existing articles by path, but a
file that moved is recognised by the URL it resolves to, so renumbering
01-install.md to 03-install.md updates the article in place — same page,
same link. Renaming the part of the filename that becomes the URL is a
different article; see Pushing.