Paycose Docs
Contributors

How to write content

Where files live, how to import from Postman, how to override generated endpoints.

Content folders

Four dirs under content/. Each has one job.

FolderSource of truthEdit by hand?
content/docs/{locale}/Guides, narrative pages (this file). Sidebar = meta.json order.Yes
content/generated/{locale}/Endpoint specs imported from Postman. <slug>.generated.json.No. Script overwrites.
content/archive/{locale}/Endpoints removed from Postman. Marked deprecated: true.No
content/manual/{locale}/Per-endpoint overrides + extras. <slug>.mdx. Slug matches generated file.Yes
content/meta/Category list (categories.json). Not localized.Yes

Slug rule: endpointId(method, route) → method-route-kebab. Example: DELETE /prices/{price_id} → delete-prices-price-id. See lib/parser/ids.ts.

Locale rule: locale is always the first segment — en/ for the default too, never a .th filename suffix. A missing th file falls back to en at load time.

Frontmatter

Guide page (content/docs/{locale}/*.mdx)

---
title: Expand
description: Short one-liner.
order: 30          # sidebar sort, lower = top
category: Guides   # optional grouping
---

Page also must appear in the meta.json pages array of its locale dir, else sidebar hide.

Manual override (content/manual/{locale}/<slug>.mdx)

---
id: delete-prices-price-id   # MUST match generated slug
title: Delete price          # overrides generated name
description: ""
notes: []
warnings: []
aliases: []
examples: []
---

Optional fields (override generated):

request:
  headers:
    - { key: Authorization, value: "Bearer {{token}}", description: "..." }
  query:
    - { key: dry_run, value: "true" }
  body:
    mode: raw
    language: json
    raw: |
      { "reason": "duplicate" }

response:
  - name: "200 OK"
    code: 200
    language: json
    body: |
      { "deleted": true }
    headers: []

Postman vars

Edit lib/constants/postman-vars.ts. Add key → value.

export const POSTMAN_VARS: Record<string, string> = {
  base_url: 'http://localhost:5002',
  api_version: 'api/v1',
};

Rules:

  • {{key}} in url/headers/body → replaced if key in map.
  • Unknown key → kept verbatim (e.g. {{price_id}}). Path params stay as placeholders.
  • Substituted once by lib/render/snippets/index.ts, then shared by every language snippet (cURL, JavaScript, Node axios, Python, Go, PHP, Java, C#, Ruby) rendered by ExampleRequest.

Import from Postman

Script writes generated/ and archive/ only. manual/ is hand-written and merged at read time. Archive branch dimmed = deprecated, still renders but excluded from sidebar.

docs/postman/Wallet.json is generated, not hand-edited. It is built from the code contract (backend/wallet/app/testdata/api_contract.golden, produced by DUMP=1 go test ./app -run TestAPIContractGolden) plus docs/postman/overlay/wallet.json, which holds the prose, folder names, example values and saved responses. make postman-check fails if the committed collection does not match the code — see docs/architecture/config-invariants.md.

To change an endpoint's docs, edit the overlay (or the Go handler), then:

python3 scripts/postman_build.py --service wallet

Then import into the docs site:

cd frontend/docs
bun scripts/import-postman.ts                 # write changes
bun scripts/import-postman.ts --dry-run       # preview only
bun scripts/import-postman.ts --source path/to/other.json

Behavior:

  • New endpoint → created in content/generated/en/, plus an untranslated copy in every other locale dir.
  • postmanHash differ → updated (overwrites en only; translated copies are left alone).
  • Hash same → skipped.
  • ID gone from collection → every locale moved to content/archive/{locale}/, marked deprecated.

Manual files never touched. Safe to re-run.

Scaffold one manual file

bun scripts/scaffold-manual.ts DELETE /prices/{price_id}       # en (default)
bun scripts/scaffold-manual.ts DELETE /prices/{price_id} th    # th override

Creates content/manual/en/delete-prices-price-id.mdx with id frontmatter pre-filled. Errors if file exists.

Override generated content

Edit content/manual/{locale}/<slug>.mdx. Merge rules (see lib/content/load-merged.ts):

Override header/query value

Match by key. Only listed keys override. Others untouched.

request:
  headers:
    - key: Authorization
      value: "Bearer prod-token-here"
      description: "Use rotated key from vault"

Generated header keeps fields you omit. Set value only → description stays from generated.

Override request body

Whole body object replaced (not field-level merge inside body):

request:
  body:
    language: json
    raw: |
      { "reason": "fraud", "notify": true }

Override responses

response array fully replaces generated array. Want both? Copy generated entries + add yours.

response:
  - name: "200 OK"
    code: 200
    language: json
    body: |
      { "id": "price_123", "deleted": true }
  - name: "404 Not Found"
    code: 404
    language: json
    body: |
      { "error": "price_not_found" }

Override title/description

title: "Cancel scheduled price change"
description: "Soft-delete a future-dated price."

Extend generated content

Sections rendered on endpoint page but not in Postman:

notes:
  - "Idempotent. Safe to retry."
  - "Quota: 100/min per service account."

warnings:
  - "Cascade deletes active subscriptions."

aliases:
  - "remove-price"

MDX body below frontmatter renders as Overview section above Request block:

---
id: delete-prices-price-id
title: Delete price
---

## When to use

Only call after subscription migration completes. Prefer `PATCH /prices/{id}` with `archived: true` for soft cases.

Add category

Edit content/meta/categories.json. id matches folderPath segment from Postman.

{
  "categories": [
    { "id": "wallet", "title": "Wallet", "order": 2 }
  ]
}

Endpoint sidebar = auto from folderPath in generated files. Reorder = re-organize Postman folders + re-import. Guide pages = listed in content/docs/meta.json pages.

Full component list: MDX blocks reference.

On this page