# Public API Docs UI

**Date:** 2026-05-31  
**Status:** Draft  
**Spec:** 74  
**Tier:** Public (no auth) + Business+ for API key creation  
**Depends on:** `white-label-api`, `zync-www-marketing-site`, `foundation-design-system`  
**Referenced by:** `white-label-api`, `zync-www-marketing-site`

---

## Overview

Public developer documentation for the Zync Tenant Public API (spec 39). Hosted at `docs.zync.is/api`. Static Astro pages generated from the OpenAPI 3.1 schema, no auth needed to browse. "Try it" feature requires a tenant API key.

Spec 39 (`tenant-public-api`) defines the API itself. This spec defines the documentation UI.

---

## Page Structure

```
docs.zync.is/api

├── /              → overview + quick-start
├── /authentication  → API keys, Bearer tokens
├── /errors          → error codes + HTTP status meanings
├── /customers       → CRUD endpoints
├── /invoices        → CRUD + state transitions
├── /time-entries    → CRUD
├── /expenses        → CRUD
├── /projects        → CRUD
├── /webhooks        → outbound webhook catalog + HMAC verification
└── /changelog       → API version history
```

---

## Layout

```
┌────────────────────────────────────────────────────────────┐
│  Zync API Docs                                v1.0  [API] │
├──────────────────┬─────────────────────────────────────────┤
│                  │                                         │
│  Overview        │  Customers                              │
│  Authentication  │                                         │
│  Errors          │  GET /v1/customers                      │
│  ─────────────── │  ─────────────────────────────────────  │
│  Resources:      │  List all customers for your tenant.    │
│  Customers       │                                         │
│  Invoices        │  Authorization: Bearer {api_key}        │
│  Time Entries    │                                         │
│  Expenses        │  Query parameters:                      │
│  Projects        │  search   string   Filter by name/email │
│  ─────────────── │  status   string   active | archived    │
│  Webhooks        │  limit    int      Max 100, default 20  │
│  Changelog       │  cursor   string   Pagination cursor    │
│                  │                                         │
│                  │  [▶ Try it]                             │
│                  │                                         │
│                  │  Response 200:                          │
│                  │  ┌─────────────────────────────────┐   │
│                  │  │  {                              │   │
│                  │  │    "customers": [...],          │   │
│                  │  │    "next_cursor": "abc123"      │   │
│                  │  │  }                              │   │
│                  │  └─────────────────────────────────┘   │
└──────────────────┴──────────────────────────────────��──────┘
```

Left nav: sticky, collapsible sections. Right panel: endpoint detail + code examples.

---

## Endpoint Documentation Pattern

Each endpoint section includes:

1. **Method + Path** — `GET /api/customers`
2. **Description** — 1–3 sentences
3. **Auth** — Always `Bearer {api_key}` + required scope (e.g. `customers:read`)
4. **Query params / Request body** — table of name, type, required, description
5. **Response schema** — JSON example (collapsible if long)
6. **Error responses** — table of HTTP codes + meaning

---

## Code Examples

Each endpoint shows a language switcher with ready-to-copy snippets:

**Languages:** `cURL`, `JavaScript (fetch)`, `Node.js`

```
[cURL]  [JavaScript]  [Node.js]

curl -X GET https://api.zync.is/v1/customers \
  -H "Authorization: Bearer zyk_live_..." \
  -H "Content-Type: application/json"
```

---

## "Try It" Feature

Requires API key. Inline form per endpoint:

```
┌─────────────────────────────────────────────────────────────┐
│  Try it                                                     │
│                                                             │
│  API Key: [zyk_live_a1b2c3d4________________]  [Save]          │
│                                                             │
│  GET /v1/customers                                          │
│  search: [_______________]                                  │
│  limit:  [20_____]                                          │
│                                                             │
│  [Send Request]                                             │
│                                                             │
│  Response (200):                                            │
│  { "customers": [...], "next_cursor": null }               │
└─────────────────────────────────────────────────────────────┘
```

API key stored in `localStorage` (prefixed key, not sent anywhere except the API). "Save" persists it across page loads.

Requests are fired from the browser directly to `api.zync.is` (no proxy). CORS is configured on `api.zync.is` to allow `docs.zync.is` origin.

---

## Webhook Catalog Page

Documents every outbound webhook event (from spec 27's event catalog): event name, payload shape, HMAC signature verification example.

```
invoice.paid

Fired when an invoice transitions to PAID status.

Payload:
{
  "event": "invoice.paid",
  "tenantId": "...",
  "data": {
    "invoiceId": "...",
    "invoiceNumber": "INV-0042",
    "amount": 1755,
    "currency": "ILS",
    "paidAt": "2026-05-31T14:22:00Z"
  }
}

Verify HMAC signature:
const sig = req.headers['X-Zync-Signature']
const expected = createHmac('sha256', webhookSecret)
  .update(req.rawBody).digest('hex')
if (sig !== expected) return 401
```

---

## Changelog Page

Static markdown file listing API changes by date:

```
## v1.0.0 — 2026-06-01
Initial public API release. Endpoints: customers, invoices, time-entries, expenses, projects.

## v1.0.1 — 2026-06-15
Added `GET /api/projects/:id/time-summary` endpoint.
```

---

## Foundation Deltas

**OpenAPI schema endpoint:** `GET /api/openapi.json` on `api.zync.is` — serves the OpenAPI 3.1 schema auto-generated from Hono route definitions via `@hono/zod-openapi`. Publicly accessible (no auth required to read the schema).

**New docs site:** `apps/zync-www/src/pages/docs/api/` — static Astro pages rendered from the OpenAPI 3.1 schema. The schema is fetched at build time.

**CORS update:** `api.zync.is` must allow `docs.zync.is` as CORS origin for "Try it" `Authorization: Bearer` requests.

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| OpenAPI 3.1 auto-generated from Hono routes | Not hand-written static docs | Spec 39 (`tenant-public-api`) uses `@hono/zod-openapi` — OpenAPI 3.1 schema is auto-generated from Zod route schemas; this is the source of truth. Docs site is generated from that schema (Redoc or custom Astro renderer), ensuring docs and implementation are always in sync. |
| Try-it fires directly to `api.zync.is` | Not a proxy | Simplest path; no additional Worker needed; CORS config on `api.zync.is` is the only requirement |
| API key in localStorage | Not sessionStorage | Developers don't want to re-paste their key every session; localStorage survives tab close |
| Hosted at `docs.zync.is/api` | Not `developers.zync.is` | Canonical URL; `docs.zync.is` subdomain is the single home for all Zync documentation; `/api` path distinguishes API docs from other docs |
