Files
SlabUploader/docs/contracts/WOO-API.md
Ty 120e0be871 docs(m3): settle deterministic title and Call 2 design
Encode the recovered per-leaf title/short-description rule and the Call 2
input set into the point of record.

- REQUIREMENTS.md: METADATA-55 now produces the description only; METADATA-60
  carries the ruled input set; METADATA-80 is the deterministic title; new
  METADATA-81 is the deterministic spec-line short description; METADATA-85
  selects the patterns; REVIEW-15 declares the primary before Call 2;
  PUBLISH-55 carries an explicit name+SKU slug.
- CONTENT-WOO.md: per-leaf title and short-description catalog, slot order,
  dimension placement, and the Cook Woods provenance.
- PROMPTS.md: Call 2 inputs and output; the deterministic patterns are cited,
  not restated.
- CREATE.md, WOO-API.md, MILESTONES.md, task_breakdown.md updated to match.

Modeled on Cook Woods product-title form; the seven per-leaf prose templates
and Specialty cuts are parked in the M3 breakdown.
2026-09-19 11:36:03 -07:00

145 lines
6.1 KiB
Markdown

# WooCommerce API contract
Status: SPEC · 2026-09-18 · external dependency: WooCommerce REST API v3.
Source: official WooCommerce REST API v3 documentation
(https://woocommerce.github.io/woocommerce-rest-api-docs/).
This contract pins the exact WooCommerce surface the app codes against.
`docs/contracts/CONTENT-WOO.md` owns the listing copy and payload intent; this file owns the wire
shape and the external constraints.
## Requirements impact
| Requirement | Impact on this design |
|---|---|
| SETTINGS-5, SETTINGS-10, SETTINGS-15 | base URL, HTTPS, app-password auth |
| SETTINGS-35, SETTINGS-50, SETTINGS-51, SETTINGS-65 | pricing, taxonomy sync, tag scope, create status |
| METADATA-5, METADATA-10, METADATA-15, METADATA-30 | categories, attributes, tags shapes |
| REVIEW-10, REVIEW-25, REVIEW-45, REVIEW-50 | name, price, SKU, field limits |
| PUBLISH-5, PUBLISH-10, PUBLISH-15, PUBLISH-20, PUBLISH-25, PUBLISH-30, PUBLISH-35 | create sequence, draft rule, errors |
| PUBLISH-40, PUBLISH-45, PUBLISH-55, PUBLISH-60 | images, payload fields, sold-individually |
| APP-35, APP-50 | error envelope mapping |
## 1. Transport and auth
- Base path: `${WOO_BASE_URL}/wp-json/wc/v3`.
- `WOO_BASE_URL` is HTTPS only and injected from env.
- Auth: WordPress application password over HTTP Basic. The consumer-key example in the Woo docs
is **not** used.
- Content type `application/json`. Dates ISO8601 (`YYYY-MM-DDTHH:MM:SS`). Monetary values are
strings with two decimals.
## 2. Endpoints used
| Purpose | Method and path |
|---|---|
| Sync categories | `GET /products/categories?per_page=100&page=N` |
| Sync tags | `GET /products/tags?per_page=100&page=N` |
| Sync attributes | `GET /products/attributes?per_page=100` |
| Sync attribute terms | `GET /products/attributes/{id}/terms?per_page=100` |
| SKU lookup | `GET /products?sku={sku}` |
| Create product | `POST /products` |
| Upload media | `POST /wp-json/wp/v2/media` (WordPress core) |
## 3. Product create field alignment
| Our value | Woo field | Woo type | Enum / constraint |
|---|---|---|---|
| title | `name` | string | none documented |
| — | `type` | string | `simple` (default) |
| create status | `status` | string | `draft`, `pending`, `private`, `publish`; default `publish` |
| sku | `sku` | string | unique; no documented format |
| slug (name + SKU) | `slug` | string | sanitized; WordPress dedupes on collision |
| price | `regular_price` | string | monetary string, 2 decimals |
| description | `description` | string | none documented |
| short_description | `short_description` | string | none documented |
| species + wood categories | `categories` | array of `{id: integer}` | leaf ids |
| fig-* / feat-* tags | `tags` | array of `{id: integer}` | tag ids |
| uploaded PNGs | `images` | array of `{id: integer}` | attachment ids, lineup order |
| five locked attributes | `attributes` | array of attribute elements | see §4 |
| — | `virtual` | boolean | default `false` |
| — | `downloadable` | boolean | default `false` |
| — | `catalog_visibility` | string | `visible`, `catalog`, `search`, `hidden`; default `visible` |
| — | `sold_individually` | boolean | default `false`; set `true` |
`sold_individually: true` is the Woo mechanism that enforces "one slab is one unit."
## 4. `attributes` element shape
Global attributes are referenced by `id` (synced from Woo), never created by name. Each element:
```json
{
"id": 8,
"position": 0,
"visible": true,
"variation": false,
"options": ["Live Edge"]
}
```
| Attribute | `options` |
|---|---|
| Edge Type | `[term name]` |
| Figure | `[term name, ...]` |
| Grade | `[term name]` |
| Thickness | `[term name]` |
| Moisture | `[term name]` |
`options` carries term **names**, not ids. `visible` is `true`; `variation` is `false`.
## 5. Categories, tags, images shapes
- `categories`: `[{"id": 9}, {"id": 14}]`
- `tags`: `[{"id": 34}]`
- `images`: `[{"id": 42}, {"id": 793}]`, in photos-ready lineup order.
- Media upload: `POST /wp-json/wp/v2/media` as `multipart/form-data` with the PNG bytes; the
response `id` is the attachment id used in `images`. Keep alpha; PNG only.
## 6. Error shape
```json
{ "code": "woocommerce_rest_term_invalid", "message": "Resource doesn't exist.", "data": { "status": 404 } }
```
`401` auth, `400` bad request, `404` missing, `500` server. These map into the app's uniform
envelope and never reach the owner raw.
## 7. Pagination
`per_page` max 100, `page` 1-based. Read `X-WP-Total` and `X-WP-TotalPages`; follow pages until
exhausted. Taxonomy lists return in full to the client.
## 8. No documented maximum lengths
The Woo v3 docs state no maximum length for `name`, `description`, `short_description`, `sku`, or
`regular_price`. Do not invent caps. The app enforces non-empty required fields and its own local
formats.
## 9. Field length limits
Hard limits are verified from WordPress/WooCommerce source; the display caps are conventions
(Google states no numeric limit).
| Field | Storage | Hard limit | Recommended display cap | Rationale |
|---|---|---|---|---|
| `name` | `wp_posts.post_title` (`text`) | 65,535 | 60 | on-screen title-tag width |
| `short_description` | `wp_posts.post_excerpt` (`text`) | 65,535 | 160 | search snippet / meta description width |
| `description` | `wp_posts.post_content` (`longtext`) | ~16.7M | none | no word-count target; write for the page |
| `sku` | `wc_product_meta_lookup.sku` (`varchar(100)`, index `sku(50)`) | 100 | 50 | index prefix length |
| `slug` | `wp_posts.post_name` (`varchar(200)`) | 200 | 200 | WordPress `wp_unique_post_slug` truncates the base near 200 |
Sources: WordPress `schema.php`, WooCommerce `class-wc-install.php`,
`class-wc-product-data-store-cpt.php`, and Google Search Central (title links, snippets, SEO
starter guide).
## 10. Media size
The store exposes its upload cap read-only as `max_upload_size` on system status. Validate the
processed PNG against it before upload and surface a plain, actionable error if exceeded.
## 11. Versioning
Woo `wc/v3` requires WooCommerce 3.5+ and WordPress 4.4+ with pretty permalinks. Breaking Woo API
changes are handled by the app's own `/api/v1` version, not by pinning the store.