API Docs

Scalar UI

Serve an interactive API reference next to your spec, and style it.

openapi.SetupDocsEndpoints serves a Scalar reference page that reads your generated spec. Readers get a searchable endpoint list, request and response schemas, and a client for trying calls.

Serve it

openapi.SetupDocsEndpoints(app)
PathContents
/docsThe Scalar reference page
/openapi.jsonThe generated document
/docs/A permanent redirect to /docs

All three are registered Public(), so they work without a token even when the rest of the app requires one.

Configure it

openapi.SetupDocsEndpoints(app, openapi.ScalarConfig{
    Title:       "Orders API",
    Theme:       "deepSpace",
    Layout:      "modern",
    DarkMode:    true,
    ShowSidebar: true,
    HideModels:  false,
    FavIcon:     "/favicon.ico",
})
FieldValuesDefault
TitleAny string, used as the page titleAPI Documentation
Themedefault, alternate, moon, purple, solarized, bluePlanet, saturn, kepler, mars, deepSpacedefault
Layoutmodern, classicmodern
DarkModeboolfalse
ShowSidebarbooltrue in the default config
HideModelsbool, hides the schema listfalse
CustomCSSCSS injected into the pageempty
FavIconURL of a faviconempty

openapi.DefaultScalarConfig() returns the defaults if you want to start from them and change one field.

Use custom paths

openapi.ServeScalar(app, "/openapi.json", "/reference", openapi.ScalarConfig{
    Title: "Orders API",
})

The first path serves the spec, the second serves the UI. The UI fetches the spec path from the browser, so it has to be reachable by whoever opens the page.

Restrict who can read the docs

Public docs are convenient internally and often unwanted in production. Two straightforward options.

Serve them only outside production:

if os.Getenv("APP_ENV") != "production" {
    openapi.SetupDocsEndpoints(app, cfg)
}

Or gate them with middleware on a group, keeping the paths but requiring a credential:

docs := app.Group("/internal").Use(basicAuth(os.Getenv("DOCS_USER"), os.Getenv("DOCS_PASS")))
docs.GET("/openapi.json", specHandler).Public()
docs.GET("/docs", uiHandler).Public()

Because ServeScalar registers on the app rather than a group, this variant means writing the two small handlers yourself, as shown in OpenAPI.

Style it

CustomCSS is injected into a <style> tag, which is enough to match a brand:

openapi.SetupDocsEndpoints(app, openapi.ScalarConfig{
    Title:    "Orders API",
    DarkMode: true,
    CustomCSS: `
        :root {
            --scalar-color-accent: #00add8;
            --scalar-font: "Inter", system-ui, sans-serif;
        }
    `,
})

Helper functions exist for building a config in steps:

cfg := openapi.DefaultScalarConfig()
openapi.ApplyScalarOptions(&cfg,
    openapi.WithCustomTheme("moon"),
    openapi.WithDarkMode(true),
    openapi.WithLayout("classic"),
)
openapi.SetupDocsEndpoints(app, cfg)

Know the constraints

The page loads the Scalar bundle from cdn.jsdelivr.net. A machine with no internet access, or a strict Content-Security-Policy, will render an empty page. If either applies, serve the spec with ezz and host the viewer yourself, or vendor the bundle and serve it from your own static assets.

The spec is regenerated on every request to the spec path. That is fine for a documentation endpoint, but do not put it on a hot path or a health check.

Next steps

  • OpenAPI for what goes into the document.
  • Testing for asserting the spec in CI.
Copyright © 2026