GitHub Actions

The Sitepaste deploy action publishes a directory of Markdown files from your repository and deploys your site. Push to main, and your changes are live in seconds. It is the natural fit for docs that live alongside code, and it works just as well for a blog kept in a repo.

Using the action requires an API token, which is a Pro plan feature.

Quick start

Create an API token in the Sitepaste dashboard under Account > Tokens (see authentication), and add it to your repository as a secret named SITEPASTE_TOKEN.

Then add a workflow at .github/workflows/deploy.yml:

name: Deploy docs

on:
  push:
    branches: [main]
    paths: [docs/**]

concurrency:
  group: sitepaste-deploy-${{ github.ref }}
  cancel-in-progress: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: sitepaste/integrations/actions/deploy@v1
        with:
          api-token: ${{ secrets.SITEPASTE_TOKEN }}
          content-dir: docs
          content-type: docs

Every run validates all files, publishes them in one batch, and triggers a deploy.

Inputs

InputRequiredDefaultDescription
api-tokenYesSitepaste API token. Store it as a repository secret.
content-dirNocontentDirectory containing the Markdown files.
content-typeNodocsContent type for all pages: docs, blog, or standalone.
site-idNoTarget site. Defaults to your workspace’s default site.
dry-runNofalseValidate and preview without publishing or deploying.
pruneNofalseDelete pages of this content type that have no matching file, making the directory the source of truth.

Outputs: page-count (pages published) and deploy-url (the deployed site’s URL).

Sections from directories

The action maps your directory structure to sections. Files at the root of the content directory have no section; files in a subdirectory use the subdirectory name:

docs/
  overview.md              → /docs/overview
  guides/
    getting-started.md     → /docs/guides/getting-started
    deployment.md          → /docs/guides/deployment

With content-type: standalone, the same layout produces top-level paths instead: guides/getting-started.md is published at /guides/getting-started. Standalone sections may also nest one level. A second directory becomes a sub-section:

api/
  overview.md              → /api/overview
  builds/
    post-builds.md         → /api/builds/post-builds

Directories beyond that depth (one level for docs and blog, two for standalone) are ignored for the section, and the action prints a warning. A section field in front matter overrides the directory, and may itself be a nested path like api/builds on standalone pages. Set it to an empty string to clear the section for a file inside a subdirectory.

Directory casing carries through as the section’s display name: a directory named API/ publishes at /api/ and displays as “API” in the navigation, exactly as if you had typed it in the dashboard. Captured casing only fills in a display name where none is set, so a rename made in the dashboard survives every deploy.

Front matter

All fields are optional. Without front matter, the slug comes from the filename and the title from the slug.

FieldDescription
slugURL slug for the page.
titlePage title.
contentTypedocs, blog, standalone, or homepage. Defaults to the workflow’s content-type input. A homepage file publishes at / and must sit at the content root.
sectionOverrides the directory-based section.
descriptionMeta description.
api_endpointMarks the page as an API reference, like GET /pages/{slug}. See API reference pages.
draftSet to true to keep the page off the deployed site.
tagsA YAML list of tags. Casing is kept as the display name: iOS is stored as ios and displays as iOS.
date or publishedAtPublish date. Date-only values like 2026-02-19 are accepted.
authorAuthor to credit the page to, as an author ID from GET /authors (not a name — names are not unique). Empty string removes the author.
og_image_urlSocial preview image URL.
languageLanguage tag for the page, like en or pt-BR.
theme, primary_color, font_size, code_theme_light, code_theme_darkPer-page theme overrides. Empty string resets a field to inherit from the site.
show_toc, show_comments, …The boolean theme overrides, in snake_case: true, false, or "inherit".

One field is deliberately missing: password. Front matter is committed to the repository, so a page password written there would be readable by anyone with repo access and preserved in git history — not a secret. The action fails the run if it finds one; set page passwords in the dashboard or through the API instead.

What the action does and does not do

It validates before sending. Slug format and length, title, description, and content size limits, tag limits, duplicate slugs, and dates are all checked locally first. If anything fails, the workflow fails with an annotation pointing at the exact file, and nothing is published.

It publishes everything, every run. Each run sends the full set of files as one batch. Pages are matched by slug, so existing pages are updated in place.

It does not delete unless you ask. By default, a file you remove from the repository stays published on your site. Set prune: 'true' to make the directory the source of truth: pages of the deployed content type with no matching file are deleted in the same run, so removed and renamed files clean up after themselves. Use pruning only when the repository owns that content type, since it will also delete pages of the same type that were created in the dashboard or from Obsidian. Combine it with dry-run to preview what would be deleted.

It always deploys. Every non-dry run ends by triggering a deploy, which counts against your monthly deploy quota. Use dry-run: 'true', for example on pull requests, to check changes without publishing.

Media is separate. Relative image references like ![](./photo.jpg) are not uploaded; the action warns about them. Upload images through the dashboard or the media API and reference them by URL.

The action source lives in the integrations repository. For the underlying API, see the pages and builds reference.