Adding API documentation

How to add or change an endpoint page on this site — the spec, the URL map, and the worked example end to end.

Every page in the API Reference section is generated from openapi.json. The playground, the code samples in seven languages, the parameter tables, the response tabs and the TypeScript definitions all come out of that one file.

That means: to change an endpoint page, edit the spec — not the page. The .mdx files under content/docs/api-reference/endpoint/ are build output and are overwritten on every build.

Prose pages work differently

Pages like Introduction and Errors are hand-written MDX and you edit them directly. This guide is about endpoint pages. See Writing prose pages at the bottom.

The one rule

Never change a public URL. Merchants have integrated against these links, support tickets reference them, and search engines have indexed them.

scripts/url-map.ts maps each operationId to its URL, and generation fails if an operation is not in that map. npm run check:urls fails if any URL would 404 or any internal link points at nothing. Both run in the build.

If you genuinely must move a page, add a redirect in next.config.ts in the same commit.


Worked example: adding a refunds endpoint

Say the API has gained POST /api/v1/refunds and it needs a page.

Describe the operation in openapi.json

Add the path. The operationId is the key everything else hangs off, so pick a stable one — it is effectively the page's permanent identifier.

openapi.json
"/api/v1/refunds": {
  "post": {
    "operationId": "createRefund",
    "tags": ["collections-stk"],
    "summary": "Refund a collection",
    "description": "Return funds to the customer who paid.\n\nRefunds are asynchronous — the response confirms the request was accepted, and the outcome arrives on your `callbackUrl`.",
    "requestBody": {
      "required": true,
      "content": {
        "application/json": {
          "schema": { "$ref": "#/components/schemas/RefundRequest" },
          "example": { "requestId": "cd3e5b37-...", "amount": 1000 }
        }
      }
    },
    "responses": {
      "201": { "description": "The refund was accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Refund" } } } },
      "400": { "description": "The collection cannot be refunded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } },
      "401": { "$ref": "#/components/responses/Unauthorized" }
    }
  }
}

summary becomes the page title and the sidebar label. description renders as the page's introduction and supports full Markdown — headings, tables, lists, links. This is where integration guidance belongs.

Add the schemas it references

Put shared shapes in components.schemas so they can be reused and so the generated TypeScript definitions stay clean.

openapi.json
"RefundRequest": {
  "type": "object",
  "required": ["requestId", "amount"],
  "properties": {
    "requestId": {
      "type": "string",
      "format": "uuid",
      "description": "The collection to refund.",
      "examples": ["cd3e5b37-8f23-465e-a720-b2a78f2b62c1"]
    },
    "amount": {
      "type": "number",
      "description": "Amount to return. Must not exceed the original collection.",
      "examples": [1000]
    }
  }
}

Reuse what already exists: TransactionStatus, CurrencyCode, Organisation, ErrorResponse, and the RequestId / CompanyId / DeviceId parameters.

Map the operationId to a URL

scripts/url-map.ts
export const OPERATION_SLUGS: Record<string, string> = {
  // ...
  createRefund: "refunds/post",
};

The value is the path under content/docs/api-reference/endpoint/, so this publishes at /api-reference/endpoint/refunds/post.

Skip this and generation stops with an explicit error. That is on purpose — a new endpoint should never quietly pick its own URL.

Put it in the sidebar

Create content/docs/api-reference/endpoint/refunds/meta.json:

meta.json
{
  "title": "Refunds",
  "icon": "Undo2",
  "pages": ["post"]
}

Then reference the folder from the parent content/docs/api-reference/endpoint/meta.json, under whichever section heading it belongs to.

icon is any lucide name in PascalCase.

Regenerate and restart

npm run generate:docs

Restart the dev server after editing the spec

The spec is parsed and cached once at startup. Hot reload will keep serving the old schema — wrong response codes, missing fields — until you restart. If a change does not show up, restart before assuming it is wrong.


Making a page genuinely useful

The difference between a page that answers questions and one that raises them is almost entirely in the spec's detail.

Describe every property

The parameter table is built from description. A property with no description renders as a bare name and type, which tells the reader nothing they could not guess.

Give more than one example

Named examples become a dropdown above the code samples. Use them wherever the right payload depends on context:

"examples": {
  "kenya":   { "summary": "Kenya (KSH)",    "value": { "currencyCode": "KSH", "...": "..." } },
  "tanzania":{ "summary": "Tanzania (TZS)", "value": { "currencyCode": "TZS", "...": "..." } }
}

Use enum for constrained values

An enum renders as a picker in the playground instead of a free-text box, and documents the valid set without prose.

Document conditional requirements in the description

required is for fields required in every case. When a field is only required sometimes — as with the currency-dependent fields on payouts — put a table in the operation description. Marking them required in the schema would make the playground reject valid requests.

Model callbacks as callbacks

Most write endpoints confirm asynchronously. A callbacks object documents the webhook payload as a first-class part of the endpoint, rather than a loose code fence readers have to hunt for:

"callbacks": {
  "transactionResult": {
    "{$request.body#/callbackUrl}": {
      "post": {
        "summary": "Refund result",
        "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCallback" } } } },
        "responses": { "200": { "description": "Acknowledged." } }
      }
    }
  }
}

Say what you do not know

If the source material is ambiguous, write that into the description rather than guessing. A documented uncertainty is useful; a confident wrong answer costs someone a day.


Writing prose pages

Anything that is not an endpoint is hand-written MDX under content/docs/. The file path is the URLcontent/docs/guides/refunds.mdx serves at /guides/refunds.

---
title: Handling refunds
description: One sentence. Used in search results, social previews and the sidebar.
icon: Undo2
---

A page only appears in the sidebar once a meta.json lists it, either by name or via ....

Components available without importing

<Callout type="warn" title="Heads up">
  Types: info, warn, error, success.
</Callout>

CardGroup is aliased to Cards, so pages carried over from Mintlify keep working unchanged.


Commands

CommandWhat it does
npm run devRegenerate reference pages, then start the dev server
npm run buildRegenerate, type-check and build for production
npm run generate:docsRebuild reference pages from openapi.json
npm run check:urlsFail if a URL would 404 or an internal link is dead
npm run types:checktsc --noEmit

Before you open a pull request

  • npm run check:urls passes
  • npm run build succeeds
  • The page renders in both light and dark mode
  • The playground actually sends a request and returns a response
  • Every new property has a description
  • Links to other docs pages are root-relative (/api-reference/...)

On this page