120e0be871
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.
145 lines
6.1 KiB
Markdown
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.
|