Skip to content

Adding an API to the API Reference

The API Reference is generated automatically from OpenAPI specifications at build time. There is no hand-written page per endpoint — you add a spec, register it in one config file, and the renderer produces the landing card, overview page, one page per tag group, one page per endpoint, the schema documentation, the sidebar, and the search records for you.

Both OpenAPI 3.x and Swagger/OpenAPI 2.0 are supported, in either JSON or YAML (.json, .yaml, or .yml). Swagger 2.0 documents are converted at build time, with build warnings for anything that cannot be converted faithfully.

There are two moving parts:

FileWhat it controls
public/openapi/*.{json,yaml,yml}The spec files themselves (raw or build-fetched)
src/config/api-reference.tsThe single source of truth — which specs are shown, their slug, title, order, and product grouping

src/config/api-sources.ts is the only site-specific file. Everything under src/lib/api-ref/ and src/components/api-ref/ is generic and reads from it.


Step 1 — Get the spec into public/openapi/

Section titled “Step 1 — Get the spec into public/openapi/”

Pick one of the two options below.

Use this when the spec is not published at a public URL, or you want to pin an exact version.

  1. Save the OpenAPI spec (JSON or YAML) into public/openapi/, for example:
    public/openapi/my-api.json # or my-api.yaml / my-api.yml
  2. That’s it for this step — the file is served as-is and is also what the “Download OpenAPI spec” button links to. Continue to Step 2.

Option B — Fetch the spec at build time (auto-fetch)

Section titled “Option B — Fetch the spec at build time (auto-fetch)”

Use this when the spec is hosted at a public URL and you want each build to pull the latest version.

  1. Open scripts/fetch-openapi.mjs and add an entry to the SOURCES array:

    const SOURCES = [
    // ...existing sources...
    {
    name: "My API", // log label only
    url: "https://app.mk.io/doc/openapi_my.json", // public spec URL
    file: "my-api.json", // written to public/openapi/
    },
    ];
  2. The fetch runs automatically in prebuild (before astro build). It is safe by design:

    • Uses ETag / If-Modified-Since caching — unchanged specs are skipped.
    • On a fetch failure, it keeps the existing file and warns, rather than breaking the build (it only hard-fails if there is no local copy at all).
    • Has a 30-second timeout per request.
  3. Commit the fetched public/openapi/my-api.json so the site still builds if the upstream URL is ever unavailable.


Step 2 — Register the spec in the config

Section titled “Step 2 — Register the spec in the config”

Open src/config/api-sources.ts and add an entry to API_SPEC_SOURCES:

export const API_SPEC_SOURCES = [
// MK.IO APIs
{ slug: 'media-api', title: 'Media API', file: 'media.json', product: 'MK.IO' },
{ slug: 'live-api', title: 'Live API', file: 'mkio-live-api.json', product: 'MK.IO' },
{ slug: 'templates-api', title: 'Templates API', file: 'mkio-templates-api.json', product: 'MK.IO' },
{ slug: 'management-api', title: 'Management API', file: 'management.json', product: 'MK.IO' },
// Beam APIs
{ slug: 'infrastructure-api', title: 'Infra API', file: 'infra.json', product: 'MK.IO Beam' },
{ slug: 'fleets-api', title: 'Fleets API', file: 'fleet.json', product: 'MK.IO Beam' },
{ slug: 'channels-api', title: 'Channels API', file: 'beam-channels-api.json', product: 'MK.IO Beam' },
]

Each entry has five fields:

FieldRequiredWhat it does
slugyesURL segment: the API lives at /api-reference/{slug}. Use kebab-case; must be unique.
titleyesThe display name in the sidebar, landing-page card, and breadcrumbs.
fileyesThe file name inside public/openapi/ (raw from Step 1A, or fetched in Step 1B). .json, .yaml, and .yml are supported.
productyesThe top-level section label — the small-caps heading in the sidebar, the landing-page section, and the product pill. Omit it and the API falls under a default “APIs” section.
groupnoOptional collapsible sub-group within the product section (e.g. 'Content'). APIs sharing the same product + group collect under one expandable sidebar item. See Nesting specs under a category.

After this, run pnpm dev (or pnpm build) and the API appears with a full set of generated pages. No other file needs to change.


The sidebar title is the title field in API_SPEC_SOURCES. Change it there — it updates the sidebar, the landing card, and the breadcrumb together.

Order in the sidebar and on the landing page

Section titled “Order in the sidebar and on the landing page”

Both the API Reference sidebar and the landing page list APIs in the exact order of the API_SPEC_SOURCES array. To move an API up or down, move its entry in the array. Product sections appear in the order in which their first member appears.

Within a single API, endpoint groups (tags), endpoints, and schemas always follow the order defined in the OpenAPI spec — they are never re-sorted alphabetically. If the order looks wrong, fix the tags / path order in the spec itself.

The product field does two things at once:

  • Renders the small-caps section heading in the sidebar (e.g. MK.IO) and the section heading on the landing page.
  • Renders the pill shown next to the API title (e.g. the MK.IO badge).

To change the pill/heading text, change the product value. The value is used verbatim, so keep capitalisation consistent across entries that should share a group ('MK.IO', 'Beam', 'Platform').

Grouping many specs under one sidebar section

Section titled “Grouping many specs under one sidebar section”

To group specs under one top-level section — for example, every Platform API under a single Platform section — give them all the same product value:

export const API_SPEC_SOURCES = [
{ slug: 'media-api', title: 'Media API', file: 'media.json', product: 'MK.IO' },
{ slug: 'live-api', title: 'Live API', file: 'mkio-live-api.json', product: 'MK.IO' },
// These three sit under one "Platform" section:
{ slug: 'platform-accounts', title: 'Accounts API', file: 'platform-accounts.json', product: 'Platform' },
{ slug: 'platform-billing', title: 'Billing API', file: 'platform-billing.json', product: 'Platform' },
{ slug: 'platform-audit', title: 'Audit API', file: 'platform-audit.json', product: 'Platform' },
]

All specs sharing a product value are listed under one section heading (in array order), each as its own expandable API row. It does not merge spec files into one page — if you need a single page spanning several files, merge them into one OpenAPI document before Step 1.

Nesting specs under a category within a section

Section titled “Nesting specs under a category within a section”

For a large product family, add a level of nesting with the optional group field. Specs that share the same product and group collapse into one expandable category inside that product section — collapsed by default, and each spec inside still expands to its own endpoints.

For example, ~10 Platform specs organised into Content and Subscriber categories:

export const API_SPEC_SOURCES = [
// ...MK.IO entries...
// Platform → Content (one collapsible category)
{ slug: 'content-search', title: 'Search API', file: 'platform/content-search.yaml', product: 'Platform', group: 'Content' },
{ slug: 'content-metadata', title: 'Metadata API', file: 'platform/content-metadata.yaml', product: 'Platform', group: 'Content' },
// Platform → Subscriber (another collapsible category)
{ slug: 'subscriber-accounts', title: 'Accounts API', file: 'platform/subscriber-accounts.yaml', product: 'Platform', group: 'Subscriber' },
{ slug: 'subscriber-billing', title: 'Billing API', file: 'platform/subscriber-billing.yaml', product: 'Platform', group: 'Subscriber' },
// A Platform spec with no group sits directly under the section (not nested)
{ slug: 'platform-status', title: 'Status API', file: 'platform/status.yaml', product: 'Platform' },
]

This renders the sidebar as three levels of nesting:

PLATFORM ← product (section heading)
▸ Content ← group (collapsible category, collapsed by default)
▸ Search API ← API spec (collapsible → its endpoints)
▸ Metadata API
▸ Subscriber ← group
▸ Accounts API
▸ Billing API
Status API ← ungrouped spec: a normal API row under the section

Notes:

  • Keep entries for the same group together in the array — a category appears at the position of its first member, and members are listed in array order.
  • A category auto-expands when it contains the page you’re currently viewing; otherwise it starts collapsed.
  • group is optional and independent per entry — mixing grouped and ungrouped specs in the same product section is fine (see Status API above), so this doesn’t change how any existing single-level APIs behave.
  • The landing page shows the same structure: each group becomes a sub-heading of its product section.
  • Sub-folders under public/openapi/ (e.g. platform/content-search.yaml) are fine — file is just the path relative to public/openapi/.

(Optional) The top-nav “API Reference” dropdown

Section titled “(Optional) The top-nav “API Reference” dropdown”

The header dropdown under API Reference is a curated shortlist and is not generated from the config. If you want the new API to appear there too, add it manually to the apiItems array in src/components/nav/SiteHeader.tsx.


Once registered, the API gets:

  • A landing-page card at /api-reference, grouped under its product.
  • An overview page at /api-reference/{slug} with description, base URL, endpoint list, a Schemas link, and a Download OpenAPI spec button.
  • One page per tag group and one page per endpoint, each with parameters, request/response schemas, multi-language code samples (cURL, JavaScript, Python, Go, Java, C#), and the interactive request builder.
  • Schema documentation at /api-reference/{slug}/schemas plus one page per component schema.
  • Sidebar entries, search records, and a static Markdown summary at /api-reference/{slug}.md.
Terminal window
pnpm dev # preview locally at http://localhost:3000/api-reference
pnpm test:api-ref # unit tests for the renderer
pnpm build # full build; watch for [api-ref] and [openapi-fetch] warnings

Build warnings prefixed [api-ref] flag spec problems (unresolved $refs, circular schemas, unconvertible Swagger 2.0 constructs). They do not fail the build — the renderer degrades gracefully — but they tell you what a given spec does not fully support.

© 2025–2026 MediaKind. All rights reserved.