What you'll build
Thally is a documentation platform that builds and hosts docs sites from a GitHub repository. In this article you'll publish a documentation site for your own project with Thally: an introduction, a quickstart, a practical guide and an API reference, live at a thally.app address, with its source in a GitHub repository you own.
I built mine for a small FastAPI helper that sends M-Pesa STK Push payment prompts. You can see the result at mpesa-stk-helper.thally.app and its source at github.com/willen-gitonga/mpesa-stk-service. Setup and publishing took me about an hour and a half, and most of the effort went into writing and checking content, not configuring Thally.
Along the way I hit a few obstacles. Each one is described where it happened, with how I resolved it.
Prerequisites
- A Thally Cloud account. A new workspace gets a one-time trial that covers up to three sites.
- A GitHub account with permission to install the Thally GitHub App on the account that will own the docs repository.
- A project to document, ideally in its own GitHub repository.
- Git and Node.js for editing and previewing the site locally. You don't need them to publish Thally's starter site. I used Node.js v22.23.3. You'll also want a plain text file open for notes. Writing down each problem as it happens makes the troubleshooting sections much easier to write later.
Step 1: Create the site in Thally Cloud
Thally's recommended path creates the site, its GitHub repository and the first deployment for you, with no local setup.
- Go to app.thally.io, sign in, and select Create a site.
- Enter a site name and choose your
thally.appaddress. Thally checks that the address is available. - Choose Create a new docs repository. When GitHub asks you to install the Thally GitHub App, the quickstart tells you to grant access to All repositories, so the app can set up the repository it creates. If your organization only allows access to selected repositories, the quickstart describes an alternative using an existing Thally repository.
- Under Product sources, connect your project's repository or choose I'll do this later. Skip Knowledge surfaces for now.
- Review the details and select Publish site. When the Home page shows the site as Live, open View live site.
Obstacle: I expected Thally to write my docs
I connected my project's repository as a product source, so I assumed the published site would contain documentation about my project. It showed Thally's starter pages instead, and I thought I'd missed a step.
Product sources feed Thally Track, which watches your project for merged changes and suggests documentation updates. When I connected my helper repo, Track also generated a first draft from a snapshot of it: Thally's GitHub app opened a pull request ("Generate product documentation") in my docs repo, with 15 changed files and a deploy preview.
How I resolved it: I treated the starter site as a working shell and the generated pull request as a first draft to check, not finished documentation. If you connected a product source, look for the same pull request in your docs repository and open its deploy preview from the Thally bot comment. Then either merge it and correct the pages afterwards, as I did, or check out its branch and fix the pages before merging. Step 3 covers what I found.
Step 2: Configure the site locally
Open the repository shown in your site summary, clone it, and start the local preview:
git clone https://github.com/YOUR-ACCOUNT/YOUR-DOCS-REPO.git
cd YOUR-DOCS-REPO
npm install
npm run dev
Three files control almost everything:
| File | What it controls |
|---|---|
src/content/*.mdx |
Each page's content, with its title and description in frontmatter |
docs.json |
Tabs, sidebar groups and page order |
src/data/site.ts |
Product name, description and links |
Set your product name and links in site.ts first. Then plan the sidebar in docs.json. Each page is listed by its path inside src/content/, without the .mdx extension, and the groups sit inside the tabs entry. Here's the structure I ended up with:
"groups": [
{ "group": "Getting Started", "pages": ["introduction", "quickstart"] },
{ "group": "Guides", "pages": ["guides/sandbox-callbacks"] },
{ "group": "API Reference", "pages": ["api/stk-push", "api/payment-status", "api/callbacks"] },
{ "group": "Reference", "pages": ["configuration"] }
]
The order follows how a reader moves through the docs: get started, complete a real task, then look up details.
Obstacle: edits to pages didn't hot reload
With npm run dev running, saving a change to an .mdx page didn't update the preview. I had to stop and restart the dev server to see each change.
I didn't find the cause within my time budget. Restarting the server worked as a workaround. If you hit this, check whether your project folder is synced by iCloud, OneDrive or Dropbox, since file watchers often miss changes in synced folders. Moving the project outside the synced folder is the usual fix.
Step 3: Write the content
Plan your pages around what a new reader needs, in the order they need it:
- Introduction: what the project does, who it's for, and what it doesn't do.
- Quickstart: the shortest path to a real result, with the expected output after each step.
- Guide: one complete task, with an example and troubleshooting.
- Reference: the details readers look up later, such as endpoints and settings. Each page is an MDX file with frontmatter at the top:
---
title: Quickstart
description: Run the helper in mock mode and send your first payment prompt.
---
In this quickstart you'll run the helper locally and send an STK Push.
For sequences, Thally's Steps component numbers each step and keeps its commands together:
<Steps>
<Step title="Start the server">
Run `uvicorn app.main:app --reload` from the project root.
</Step>
</Steps>
MDX treats curly braces and angle brackets in normal text as code, so keep things like {PUBLIC_BASE_URL} inside backticks or code blocks.
Obstacle: the generated draft was confidently wrong
The pages Thally generated from my repository were well structured and mostly accurate. But when I checked each claim against the code, I found problems a reader would have hit:
- A description saying tokens refresh automatically. The code only fetches a new token when the cached one is close to expiring.
- A dependency table with a wrong FastAPI version, duplicating
requirements.txt. - Setup steps that named the virtual environment
virtual, which.gitignoredidn't ignore, so readers would have committed it. - Expected server output that didn't match what the server actually prints.
- A claim that valid Kenyan numbers starting with
2546were rejected, and an unverified claim that Safaricom retries callbacks. - No practical guide at all, only reference pages. How I resolved it: In a follow-up pull request, I rewrote the introduction and quickstart, moved the sandbox setup into a new guide, and deleted the generated changelog. Track's draft also included a reference page for each API endpoint, and I kept those after checking them against the code.
Testing my own commands caught one more problem. I ran uvicorn main:app out of habit and got Could not import module "main", because the app lives in app/main.py. The correct command is uvicorn app.main:app, run from the project root. I added the error to the quickstart's troubleshooting table, since readers will make the same mistake.
The lesson: treat generated docs as a first draft, and only publish output you've seen in your own terminal.
Step 4: Publish your changes
Thally publishes whatever is on your production branch, so work on a separate branch and merge through a pull request.
- Create a branch and commit each logical change separately:
git checkout -b rewrite-docs
git add src/content/introduction.mdx
git commit -m "Rewrite introduction for accuracy and audience"
- Before pushing, validate the site and make sure it builds:
npx --yes @thallylabs/cli@latest check
npm run build
The check catches problems such as a page listed in docs.json that doesn't exist.
- Push the branch and open a pull request into your production branch:
git push -u origin rewrite-docs
- Before merging, open the deploy preview. Thally posts it as a bot comment on the pull request, and it's easy to miss under the CI checks. I missed it, and the comment's link was removed once I merged, so I checked my changes locally with
npm run devinstead. - Merge the pull request. Thally builds and publishes the new version, and the deployment appears in your site's deployment history. My live site updated after the merge. Finally, open your live site and click through every page and link once more.
What I'd tell you before you start
- Keep notes as you go. Every obstacle in this article came from a note I wrote in the moment. They became my troubleshooting rows.
- Test your quickstart in a fresh folder. Reading it isn't enough. Running the commands myself is how I caught the import error.
- Keep it small. An introduction, one quickstart and one guide are enough for a useful site. Add reference pages only where readers will look things up. You now have a live documentation site and a workflow for updating it: edit on a branch, check, preview and merge. My finished site is at mpesa-stk-helper.thally.app if you want to compare.
Top comments (0)