# PMC MCP Protocol

This file is published to <a href="https://mcp.g3nretailstack.com/pmc/PROTOCOL.md" target="_blank" rel="noopener noreferrer">https://mcp.g3nretailstack.com/pmc/PROTOCOL.md</a>.


## Usage patterns (headless)
- Stack-wide SOPs & operations catalog: <a href="https://doc.g3nretailstack.com/story/operations.html" target="_blank" rel="noopener noreferrer">/story/operations.html</a>.
- Super-usecase scenarios + QA status: <a href="https://doc.g3nretailstack.com/story/super-usecases.html" target="_blank" rel="noopener noreferrer">/story/super-usecases.html</a>.
- This protocol stays contract-only; use the catalogs for workflow expectations.

## Base URL
- API Gateway: `https://api.g3nretailstack.com/pmc`
- Health check: `GET /pmc/stat` — requires `requireSession` (any authenticated user).

## Auth + tenancy
- Auth placement: header auth is canonical for org-scoped APIs; body auth is accepted for compatibility where documented. Exceptions: USM and UTL require body auth. See [/common/headers-identity.html](https://doc.g3nretailstack.com/common/headers-identity.html).
- Every tenant call requires `x-orgcode`.
- Auth is either:
  - `x-session-guid` (user session), OR
  - `x-api-key` (org-bound service account)
- Non-associated callers receive `404 not-found` (anti-enumeration).
- Optional cost attribution: provide `x-cccode` (or request field `cccode`). Format `^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$`, canonicalized to uppercase. Invalid values (or body/header mismatch) return HTTP 400. When omitted, usage is attributed to the org.
- `session_guid` is never emitted in responses; use `stats.session_fingerprint` for correlation.
- Query semantics:
  - Serving list filters are exact-match only (no scans).
  - Partial-match search is available via `GET /product/search` (prefix/contains/regex), backed by PMC’s search plane (OpenSearch Serverless).

## Roles
- Read surfaces require `pmc_view` (owner implied; accepts `pvv` and `pma` synonyms).
- Write surfaces require `pmc_publish` (owner implied; accepts `pma` synonym).

## Surfaces

### Build
- `GET /build/stats`

### Product (serving)
- `GET /product/get` (by `product_id` OR `variant_id + channel_guid`)
- `GET /product/list` (one filter at a time; optional `online_state=online|offline|both` (legacy `only_online=true`); paginates with `next_token`)
  - Supports ID facets (e.g., `style_id`, `vendor_id`, `channel_guid`) and exact-match lookup keys (e.g., `variant_code`, `sku`, `barcode_value`, `identifier_type+identifier_value`, `alias_tag+alias_value`) per OpenAPI.
- `GET /product/search` (partial match; `q` + optional `field=any|sku|code|barcode|identifier|alias` + `mode=prefix|contains|regex`; supports `online_state=online|offline|both` and optional `channel_guid`/`logical_guid` filters)
  - Guardrails: `contains` requires `q` length ≥ 3; `prefix` ≥ 2; `barcode` is digits-only with ≥4 digits; `regex` mode requires `pmc_publish` role (or owner) — returns `403 forbidden-role` otherwise.
- `POST /product/group` (bounded group-by counts; `dimension` required, optional `values[]` and `online_state`; category/department/division require `channel_guid`/`channel_code`/`logical_guid`; rollup-backed for `values[]` with search-plane fallback)
- `GET /product/revision/list`
- `POST /product/online/set` (requires `expected_revision`; optional `reason_code`)
- `POST /product/online/clear` (requires `expected_revision`; optional `reason_code`)
- `GET /product/pointer/get` (staged/preview/fallback)
- `POST /product/pointer/set` (requires `rev` + `rev_id`)
- `POST /product/pointer/clear` (requires `expected_revision`)

### Content packs
- `POST /pack/create` (inline JSON or presigned upload)
- `POST /pack/complete` (finalize presigned upload)
- `GET /pack/get`
- `GET /pack/list` (by `pack_id`)

### Publishability profiles
- `GET /publish-profile/get` (per `channel_code`)
- `POST /publish-profile/set` (creates/updates; updating requires `expected_revision`)

### Publish runs (client-driven)
Publish orchestration is client-driven:
1) `POST /publish/run/start` → returns `run_id`, `revision`, and `status=running`
2) Loop `POST /publish/run/step` with `run_id` + `expected_revision` until it returns `status=completed`
3) Optional: `GET /publish/run/get` at any time
4) Manifests: `GET /publish/run/manifest/list` then `GET /publish/run/manifest/presign` for part downloads

Selector note:
- If publishing a single variant, provide both `variant_id` and `style_id` (PVM variant lookup is style-scoped).

### Publish run cancel
- `POST /publish/run/cancel` — cancels a running publish run. Requires `run_id` + `expected_revision`.

## Error tags
Common tags (see [/common/error-tags.html](https://doc.g3nretailstack.com/common/error-tags.html) for definitions): `invalid-input`, `unauthorized`, `forbidden`, `not-found`, `expected-revision-required`, `conflict`, `invalid-state`, `not-implemented`, `upstream-failed`, `throttled`, `internal-error`.

## Example (curl)

```bash
export PMC_BASE="https://api.g3nretailstack.com/pmc"
export ORGCODE="PMXXXXXXX"
export SESSION_GUID="..."

curl -sS "${PMC_BASE}/product/list?online_state=online&limit=8" \\
  -H "x-orgcode: ${ORGCODE}" \\
  -H "x-session-guid: ${SESSION_GUID}" | jq

curl -sS "${PMC_BASE}/product/search?q=abc&mode=contains&online_state=both&limit=8" \\
  -H "x-orgcode: ${ORGCODE}" \\
  -H "x-session-guid: ${SESSION_GUID}" | jq
```


## Endpoint inventory (OpenAPI parity)
The endpoints below are implemented and defined in `/pmc/openapi.yaml`. Request/response schema names reference OpenAPI component schemas.

| Method | Path | Request schema | Response schema |
| --- | --- | --- | --- |
| GET | /build/stats | — | (inline) |
| POST | /pack/complete | (inline) | Envelope |
| POST | /pack/create | (inline) | (inline) |
| GET | /pack/get | — | (inline) |
| GET | /pack/list | — | (inline) |
| GET | /product/get | — | (inline) |
| POST | /product/group | (inline) | (inline) |
| GET | /product/list | — | (inline) |
| POST | /product/online/clear | (inline) | Envelope |
| POST | /product/online/set | (inline) | (inline) |
| POST | /product/pointer/clear | (inline) | Envelope |
| GET | /product/pointer/get | — | (inline) |
| POST | /product/pointer/set | (inline) | (inline) |
| GET | /product/revision/list | — | (inline) |
| GET | /product/search | — | (inline) |
| GET | /publish-profile/get | — | (inline) |
| POST | /publish-profile/set | (inline) | Envelope |
| POST | /publish/run/cancel | (inline) | Envelope |
| GET | /publish/run/get | — | (inline) |
| GET | /publish/run/manifest/list | — | (inline) |
| GET | /publish/run/manifest/presign | — | Envelope |
| POST | /publish/run/start | (inline) | (inline) |
| POST | /publish/run/step | (inline) | Envelope |

## Idempotency & retries
- All **GET / list / resolve / search** calls are safe to retry with identical inputs (read-only, no side effects).
- **POST mutations** that accept `expected_revision` use optimistic concurrency: on `409 conflict` or `428 expected-revision-required`, re-read the record, obtain the current `revision`, and retry with the updated value.
- Creates are generally **not** idempotent. Prefer caller-provided `code` (where supported) and verify existence before retrying a failed create.
- Bulk or scheduled jobs that accept an `idempotency_key` will de-duplicate within the documented time window.

## Known pitfalls
- **Missing `expected_revision`**: most state-changing operations require it; omitting it returns `428` with the current revision in `error.details`.
- **Stale revision**: reading a record, waiting, then writing with an outdated `revision` triggers `409`. Always use the latest revision from the most recent read.
- **Pagination cursors**: `next_token` is opaque JSON. Do not modify, decode, or persist cursors across sessions — they may change format between deploys.
- **Anti-enumeration 404**: some org-scoped reads return `404` even when the record exists, if the caller is not associated with the org. Treat `404` as ambiguous; verify caller association before assuming "not found".

## OpenAPI
- Contract schema: <a href="https://doc.g3nretailstack.com/pmc/openapi.yaml" target="_blank" rel="noopener noreferrer">https://doc.g3nretailstack.com/pmc/openapi.yaml</a>


_Build MONDAY-1776194870 • 2026-04-14T19:27:50.000Z • [© 1999 Microhouse Systems Inc. All rights reserved.](https://doc.g3nretailstack.com/common/copyright-license.html)_
