Skip to content

Commit aa3ef54

Browse files
committed
add admin api and webhook generation guides
1 parent f851bd9 commit aa3ef54

File tree

2 files changed

+278
-0
lines changed

2 files changed

+278
-0
lines changed

.claude/rules/admin-api-docs.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Admin API & Webhooks Reference Generation
2+
3+
## Overview
4+
5+
The Admin API, Campaigns API, and Webhook reference docs are generated from OpenAPI YAML specs through a two-phase pipeline: Python tools download and enrich the specs, then a Node.js script generates MDX pages for fumadocs.
6+
7+
## Pipeline
8+
9+
### Phase 1: Python — Schema Download & Webhook Generation
10+
11+
Run manually when API specs change:
12+
13+
```bash
14+
cd tools && python update_api_docs.py
15+
```
16+
17+
**Scripts:**
18+
- `tools/update_api_docs.py` — Downloads OpenAPI specs from live endpoints, injects webhook schemas, applies overlays (server definitions, auth, descriptions), saves to `public/api/`
19+
- `tools/config.py` — Central configuration: API versions, remote URLs, webhook event registry (21 events), custom payloads, server/auth overlays
20+
- `tools/webhooks.py` — Generates webhook event schemas by resolving `$ref` chains from REST API object schemas, flattening `allOf`/`oneOf`, and building example payloads
21+
22+
**Remote spec sources:**
23+
- Admin API: `https://sandbox.29next.store/api/schema/admin/`
24+
- Campaigns API: `https://campaigns.apps.29next.com/api/schema/`
25+
26+
**Output:** YAML specs in `public/api/`:
27+
- `public/api/admin/2024-04-01.yaml` (stable)
28+
- `public/api/admin/2023-02-10.yaml` (legacy)
29+
- `public/api/admin/unstable.yaml`
30+
- `public/api/campaigns/v1.yaml`
31+
32+
### Phase 2: Node.js — MDX Generation
33+
34+
Runs automatically before every `npm run dev` and `npm run build`:
35+
36+
```bash
37+
node scripts/generate-api-docs.mjs
38+
```
39+
40+
**Script:** `scripts/generate-api-docs.mjs` (455 lines)
41+
42+
**Steps:**
43+
1. **Fix webhook schemas** — Normalizes malformed schemas from Python output (adds `type: object`, fixes array-style types, generates missing examples)
44+
2. **Clean stale directories** — Removes old versioned layout dirs
45+
3. **Generate REST endpoint pages** — Calls `fumadocs-openapi` `generateFiles()` per spec, grouped by tag, excluding webhook files (detected by dot notation in filename like `cart.abandoned`)
46+
4. **Generate webhook event pages** — Same `generateFiles()` but keeps only webhook files
47+
5. **Write `meta.json` files** — Sets tag order from spec, marks version subdirs with `root: true`
48+
6. **Extract HTTP methods**`lib/generated/api-methods.json` (for sidebar method badges)
49+
7. **Collect endpoint data**`lib/generated/api-endpoints.json` (for AI search indexing)
50+
51+
## Output Structure
52+
53+
```
54+
content/docs/admin-api/reference/ ← Stable (2024-04-01)
55+
├── apps/, carts/, orders/, ... ← Tag folders with .mdx files
56+
├── 2023-02-10/ ← Legacy version (root: true)
57+
├── unstable/ ← Dev version (root: true)
58+
└── meta.json
59+
60+
content/docs/campaigns/api/ ← Campaigns (v1)
61+
├── address/, campaigns/, carts/, orders/
62+
└── meta.json
63+
64+
content/docs/webhooks/reference/ ← Webhook events
65+
├── apps/, carts/, orders/, ...
66+
├── 2023-02-10/ ← Legacy version (root: true)
67+
├── unstable/ ← Dev version (root: true)
68+
└── meta.json
69+
70+
lib/generated/
71+
├── api-methods.json ← URL → HTTP method map (sidebar badges)
72+
└── api-endpoints.json ← Endpoint metadata (AI search)
73+
```
74+
75+
## Versioning Strategy
76+
77+
- **Stable** content lives in the base output directory (no `root: true`)
78+
- **Versioned** directories (2023-02-10, unstable) get `root: true` in `meta.json` so fumadocs treats them as alternate section roots
79+
- Versioned subdirs are NOT listed in the parent `pages` array — fumadocs auto-discovers them
80+
- `DocsLayout` sidebar transform in `app/docs/layout.tsx` filters versioned paths from the section tab dropdown
81+
82+
## Key Files
83+
84+
| File | Purpose |
85+
|------|---------|
86+
| `scripts/generate-api-docs.mjs` | Main MDX generation orchestrator |
87+
| `tools/config.py` | API versions, webhook registry, server/auth overlays |
88+
| `tools/update_api_docs.py` | Downloads and enriches OpenAPI specs |
89+
| `tools/webhooks.py` | Resolves REST schemas into webhook event payloads |
90+
| `components/api-page.tsx` | Two-column React renderer (`APIPage` from fumadocs-openapi) |
91+
| `lib/source.ts` | Sidebar method badges (GET=green, POST=blue, PUT=orange, PATCH=yellow, DELETE=red) |
92+
93+
## Generated MDX Format
94+
95+
**REST endpoint:**
96+
```mdx
97+
---
98+
title: Carts Create
99+
description: Create a new cart.
100+
full: true
101+
_openapi:
102+
method: POST
103+
toc: []
104+
---
105+
<APIPage document={"public/api/admin/2024-04-01.yaml"} operations={[{"path":"/carts/","method":"post"}]} />
106+
```
107+
108+
**Webhook event:**
109+
```mdx
110+
---
111+
title: cart.abandoned
112+
description: Triggers when a cart is abandoned.
113+
_openapi:
114+
method: POST
115+
webhook: true
116+
---
117+
<APIPage document={"public/api/admin/2024-04-01.yaml"} webhooks={[{"name":"cart.abandoned","method":"post"}]} />
118+
```
119+
120+
## Important Conventions
121+
122+
- REST endpoint filenames use camelCase (`cartsCreate.mdx`), webhook events use dot notation (`cart.abandoned.mdx`) — this is how the script distinguishes them
123+
- Tag ordering in the sidebar follows the order tags appear in the OpenAPI spec
124+
- The `APIPage` component renders a two-column layout: docs on the left, example requests/responses on the right (matching the GraphQL operation pages)
125+
- All generated files are committed to git (not gitignored)
126+
- `components/api-page.tsx` loads all 4 spec files at import time via `createOpenAPI()`
127+
- TypeScript definition generation is disabled (`generateTypeScriptDefinitions: () => undefined`)
128+
129+
## Adding a New API Version
130+
131+
1. Add the version config to `tools/config.py` `API_VERSIONS` array
132+
2. Run `python tools/update_api_docs.py` to download the spec
133+
3. Add the spec path to `scripts/generate-api-docs.mjs` in the `specs` array
134+
4. Add the spec to `components/api-page.tsx` `createOpenAPI({ input: [...] })`
135+
5. Run `npm run generate-api-docs` to generate pages
136+
137+
## Adding a New Webhook Event
138+
139+
1. Add the event to the `WEBHOOKS` list in `tools/config.py` with: event name, object type, schema reference, tag, description
140+
2. Run `python tools/update_api_docs.py` to regenerate specs with the new webhook
141+
3. Run `npm run generate-api-docs` to generate the webhook page

.claude/rules/webhooks-docs.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Webhooks Documentation
2+
3+
## Overview
4+
5+
Webhook reference docs are generated alongside the Admin API docs. The pipeline takes REST API object schemas from the Admin API OpenAPI specs, wraps them in a webhook envelope structure, and generates per-event reference pages with example payloads.
6+
7+
The hand-written webhook overview page at `content/docs/webhooks/index.mdx` is maintained separately and is NOT generated — it contains the events table, payload structure docs, verification guide, and versioning info.
8+
9+
## Generation Pipeline
10+
11+
### Step 1: Python — Schema Generation (`tools/webhooks.py`)
12+
13+
Called by `tools/update_api_docs.py` during spec download. For each webhook event defined in `tools/config.py`:
14+
15+
1. **Resolves the schema** — Looks up the `schema_ref` (e.g. `#/components/schemas/Order`) from the Admin API spec
16+
2. **Cleans the schema** — Removes `readOnly` flags, recursively resolves `$ref`, `allOf`, `oneOf` references (depth limit: 4)
17+
3. **Generates example payload** — Builds realistic JSON example with format-aware defaults (UUIDs, dates, emails, etc.)
18+
4. **Wraps in envelope** — Creates the full webhook payload structure:
19+
20+
```json
21+
{
22+
"api_version": "2024-04-01",
23+
"object": "order",
24+
"data": { ... resolved object schema ... },
25+
"event_id": "a7a26ff2-...",
26+
"event_type": "order.created",
27+
"webhook": { "id": 1, "store": "example", "events": ["order.created"], "target": "https://example.com/webhook/" }
28+
}
29+
```
30+
31+
5. **Injects into spec** — Adds the webhook to the OpenAPI spec's `webhooks` section, which fumadocs-openapi then renders
32+
33+
### Step 2: Node.js — MDX Generation (`scripts/generate-api-docs.mjs`)
34+
35+
1. **`fixWebhookSchemas()`** — Normalizes any malformed schemas from Python output (adds missing `type: object`, fixes array-style types, generates missing examples)
36+
2. **`webhookBeforeWrite()`** — Filters `generateFiles()` output to keep ONLY webhook files (dot-notation filenames like `cart.abandoned.mdx`)
37+
3. **Generates per-event MDX pages** grouped by tag into `content/docs/webhooks/reference/{tag}/`
38+
4. **Writes `meta.json`** with tag ordering from the spec
39+
40+
## Webhook Event Registry
41+
42+
All 22 events are defined in `tools/config.py` `WEBHOOKS` list. Each entry:
43+
44+
```python
45+
{
46+
"event": "order.created", # Dot-notation event name
47+
"object": "order", # Object type label
48+
"schema_ref": "#/components/schemas/Order", # Admin API schema to resolve
49+
"tag": "orders", # Sidebar grouping
50+
"description": "Triggers when a new order is created.",
51+
}
52+
```
53+
54+
**Events by tag:**
55+
- **apps**: `app.uninstalled`
56+
- **carts**: `cart.abandoned`
57+
- **customers**: `customer.created`, `customer.updated`
58+
- **fulfillment**: `fulfillment.created`, `fulfillment.updated`
59+
- **orders**: `order.created`, `order.updated`
60+
- **payments**: `dispute.created`, `dispute.updated`, `gateway.created`, `gateway.updated`, `transaction.created`, `transaction.updated`
61+
- **products**: `product.created`, `product.updated`, `product.deleted`
62+
- **subscriptions**: `subscription.created`, `subscription.updated`
63+
- **store**: `store.updated`
64+
- **support**: `ticket.created`, `ticket.updated`
65+
66+
### Custom Payloads
67+
68+
Some events don't follow their object's schema. These are defined in `CUSTOM_WEBHOOK_EVENT_PAYLOADS`:
69+
70+
- `product.deleted` — Only includes `{ id: integer }` (no full product data since the product is deleted)
71+
72+
Set `schema_ref: None` in the webhook config and add a custom payload entry.
73+
74+
## Output Structure
75+
76+
```
77+
content/docs/webhooks/
78+
├── index.mdx ← Hand-written overview (NOT generated)
79+
├── meta.json ← { root: true, pages: ["index", "reference"] }
80+
└── reference/
81+
├── apps/
82+
│ └── app.uninstalled.mdx
83+
├── carts/
84+
│ └── cart.abandoned.mdx
85+
├── orders/
86+
│ ├── order.created.mdx
87+
│ └── order.updated.mdx
88+
├── payments/
89+
│ ├── dispute.created.mdx
90+
│ ├── gateway.created.mdx
91+
│ └── transaction.created.mdx
92+
├── 2023-02-10/ ← Legacy version (root: true)
93+
├── unstable/ ← Dev version (root: true)
94+
└── meta.json
95+
```
96+
97+
## Generated MDX Format
98+
99+
```mdx
100+
---
101+
title: order.created
102+
description: Triggers when a new order is created.
103+
full: true
104+
_openapi:
105+
method: POST
106+
webhook: true
107+
---
108+
<APIPage document={"public/api/admin/2024-04-01.yaml"} webhooks={[{"name":"order.created","method":"post"}]} />
109+
```
110+
111+
The `APIPage` component renders a two-column layout: webhook payload schema on the left, example JSON payload on the right.
112+
113+
## Key Conventions
114+
115+
- Webhook filenames use **dot notation** (`order.created.mdx`) vs REST endpoints which use camelCase (`ordersCreate.mdx`) — this is how `isWebhookFile()` distinguishes them during generation
116+
- The webhook overview page (`content/docs/webhooks/index.mdx`) is hand-maintained and includes the events table, payload structure, verification code, and versioning docs — do not overwrite it
117+
- Webhook payload `data` schemas mirror the Admin API's object schemas (serializers), so webhook data matches what you'd get from a GET request on the corresponding REST endpoint
118+
- Webhook response codes: `200` = success, `410` = auto-disable the webhook
119+
- Verification: `X-29Next-Signature` header contains HMAC-SHA256 of the payload signed with the webhook signing secret
120+
- Retry policy: up to 10 retries over several days with exponential backoff; failing webhooks trigger admin email notifications and eventual deactivation
121+
122+
## Adding a New Webhook Event
123+
124+
1. Add the event to `tools/config.py` `WEBHOOKS` list with: event name, object type, schema_ref, tag, description
125+
2. If the event has a non-standard payload, add it to `CUSTOM_WEBHOOK_EVENT_PAYLOADS` and set `schema_ref: None`
126+
3. Run `cd tools && python update_api_docs.py` to regenerate the OpenAPI specs with the new webhook
127+
4. Run `npm run generate-api-docs` to generate the webhook MDX page
128+
5. Update the events table in `content/docs/webhooks/index.mdx` with the new event row
129+
130+
## Versioning
131+
132+
Webhook versions follow Admin API versions. Each version gets its own subdirectory with `root: true`:
133+
- Stable (2024-04-01) → `content/docs/webhooks/reference/` (base directory)
134+
- Legacy (2023-02-10) → `content/docs/webhooks/reference/2023-02-10/`
135+
- Unstable → `content/docs/webhooks/reference/unstable/`
136+
137+
The `api_version` field in webhook payloads matches the Admin API version the webhook was configured with.

0 commit comments

Comments
 (0)