Publishing

GitHub (Static Sites)

Available

Publish to any static site — Next.js, Astro, Hugo, Jekyll, Gatsby, Eleventy — by committing markdown files with YAML frontmatter to a GitHub repo. Your existing CI pipeline rebuilds the site. No middleware, no runtime, no vendor lock-in.

Prerequisites

  • A GitHub repository with your static site source.
  • A CI/CD setup that rebuilds on push (Vercel, Netlify, Cloudflare Pages, GitHub Pages — all auto-rebuild by default).
  • A Fine-Grained Personal Access Token with Contents: Read and write permission for this single repo.

Setup guide

  1. Go to GitHub → Settings → Developer settings → Fine-grained tokens.
  2. Click Generate new token. Name it KwikScaleAI.
  3. Set Repository access to Only select repositories, pick the repo you want to publish to.
  4. Under Repository permissions, set Contents to Read and write. Leave all other permissions at “No access.”
  5. Generate the token, copy it. You cannot see it again.
  6. In KwikScaleAI: Integrations → GitHub → Connect. Fill in owner, repo, branch, content path, and paste the PAT.
  7. Click Test connection. KwikScaleAI checks that the repo exists, the token has the right permission, and the branch is valid.

How it works

For each approved article, KwikScaleAI makes two or three HTTP calls to GitHub's Contents API:

  1. GET /repos/{owner}/{repo}/contents/{path}— look up the current SHA (only on updates). A 404 means the file doesn't exist yet → treat as create.
  2. PUT /repos/{owner}/{repo}/contents/{path} — create or update the file. Base64-encoded content, configurable commit author and message.

Auth is Authorization: Bearer {personalAccessToken}. The commit triggers your CI, your site rebuilds, the article goes live. KwikScaleAI stores the file path as the cmsPostId so updates hit the same file on re-publish.

Frontmatter format

Every article is committed as a markdown file with a YAML frontmatter block at the top. The standard fields below cover most SSG themes — see supported SSG notes below for any theme-specific quirks.

content/blog/how-we-doubled-organic-traffic.md
---
title: "How We Doubled Organic Traffic in 90 Days"
slug: "how-we-doubled-organic-traffic"
date: "2026-04-16T12:00:00.000Z"
description: "The exact playbook we used to 2x traffic..."
tags: ["seo", "case-study", "growth"]
draft: false
---

# How We Doubled Organic Traffic in 90 Days

Lorem ipsum dolor sit amet, consectetur adipiscing elit...

Configuration

ownerRequired

string

GitHub username or organization that owns the repo.

repoRequired

string

Repository name (without the owner prefix).

branchOptional

string

Branch to commit to. Must already exist — the adapter does not create branches.

Default: main

contentPathRequired

string

Directory inside the repo where markdown files go, e.g. content/blog, src/content/posts, _posts (Jekyll). Leading/trailing slashes are stripped.

personalAccessTokenRequired

string

Fine-Grained PAT with Contents: Read and write on this repo.

fileExtensionOptional

'md' | 'mdx'

md for standard markdown, mdx for Astro, Next.js MDX, Docusaurus, etc.

Default: md

commitAuthorNameOptional

string

Name shown on the commit in git log.

Default: KwikScaleAI

commitAuthorEmailOptional

string (email)

Email shown on the commit.

Default: bot@kwikscaleai.com

liveUrlPatternOptional

string (URL)

Pattern for the public URL the article will have after rebuild, with {slug} as the placeholder. Example: https://example.com/blog/{slug}. When set, the dashboard shows a “View live” link. When unset, we fall back to the GitHub blob URL.

Supported static site generators

SSGTypical contentPathNotes
Next.js (MDX)src/content/blog or postsSet fileExtension: mdx. Works with Contentlayer, MDX Remote, and native Next 15 MDX.
Astrosrc/content/blogMatches Astro's default content collections. Use fileExtension: md or mdx.
Hugocontent/postsHugo expects draft: false (which we emit) and date fields — both covered.
Jekyll_postsJekyll requires the filename to start with a date, e.g. 2026-04-16-slug.md. Currently we name files {slug}.md — for Jekyll, a post-processing step in CI is needed. Not fully supported in v1.
Gatsbycontent/blogUses the same frontmatter Gatsby's gatsby-transformer-remark reads.
Eleventy (11ty)src/postsUse the tags frontmatter; 11ty collections pick it up automatically.

Configuring the live URL pattern

Set liveUrlPattern so the dashboard links to the public article URL — not the GitHub blob URL — after rebuild. Replace {slug} with wherever the slug appears in your URL structure:

  • Blog under root: https://example.com/{slug}
  • Blog under path: https://example.com/blog/{slug}
  • Dated URLs: https://example.com/blog/2026/{slug}— note the year must be static; dynamic date substitution isn't in v1.

Capabilities

Update existing
Draft
Tags
Categories
Featured image
Scheduled publish
Delete

Troubleshooting

Test connection returns 401 or 404
The PAT is wrong, expired, or doesn't have access to this repo. Regenerate a Fine-Grained token scoped to just this repo with Contents: Read and write, paste it fresh, and re-test.
Test connection says 'PAT missing push permission'
Fine-Grained PAT was created with Contents: Read only. Edit the token (or create a new one), flip Contents to Read and write.
Publishes succeed but the site doesn't rebuild
Your CI isn't wired to this branch, or it filters by commit path. Check your CI config (.github/workflows, netlify.toml, vercel.json) and make sure pushes to the configured branch trigger a build. Also check that your CI isn't skipping commits from the KwikScaleAI bot account based on author email.
409 Conflict on update
The file changed between the SHA fetch and the PUT (someone — or a GitHub Action — edited the same file in parallel). v1 doesn't auto-retry; trigger a manual re-publish from the dashboard and the next SHA round-trip will succeed.
The file ends up in the wrong folder
contentPath is wrong. It should be relative to the repo root, no leading or trailing slash needed (we strip them anyway). Example: for Astro, use 'src/content/blog', not '/src/content/blog/'.
PAT works for reads but 403 on writes
If the repo is in an organization that enforces branch protection rules, writes from bots might be blocked. Either: (1) whitelist the bot in branch protection, (2) target an unprotected branch and merge via PR, or (3) switch to a deploy key or GitHub App (not supported in v1).

FAQ

Which static site generators work with this integration?
Any SSG that reads markdown or MDX files with YAML frontmatter: Next.js (with contentlayer, MDX Remote, or gray-matter), Astro, Hugo, Jekyll, Gatsby, Eleventy, Docusaurus, VitePress, Zola, Nuxt Content. The standard frontmatter fields (title, slug, date, description, tags, draft) cover ~95% of themes.
Do I need to use a Fine-Grained PAT or a Classic PAT?
Fine-Grained is recommended — you can scope the token to the single repo KwikScaleAI needs, reducing blast radius if it leaks. Classic PATs grant org-wide access. Either works; our call only requires repo Contents: read + write.
Can I commit to a non-default branch?
Yes. Set the 'branch' config to whatever you want — e.g. 'staging' for a preview environment, or a separate 'content' branch that merges into main via CI. The branch must exist; the adapter doesn't create branches.
What happens when KwikScaleAI updates an existing article?
We GET the file's current SHA, then PUT the updated content with that SHA in the body. GitHub rejects the PUT if the file changed since we read the SHA (optimistic concurrency). That means your CI or a human can't be in the middle of editing the same file — but that scenario is rare.
Is there a commit per article, or does KwikScaleAI batch?
One commit per article in v1. Each commit uses the author name and email you configure (default: KwikScaleAI bot). Batched multi-file commits via the Git Trees API are on the roadmap.

Ready to automate publishing?

Connect your site once. KwikScaleAI researches, writes, and publishes SEO content on autopilot.

Get started free