# PCM MCP Protocol

This file is published to <a href="https://mcp.g3nretailstack.com/pcm/PROTOCOL.md" target="_blank" rel="noopener noreferrer">https://mcp.g3nretailstack.com/pcm/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/pcm`
- Health check: `GET /pcm/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`).
- `session_guid` is never emitted in responses; use `stats.session_fingerprint` for correlation.
- Facility context: `x-logical-guid` (required for operational actions; see OpenAPI per-path). `x-channel-code` (required where documented; see OpenAPI per-path).

## Roles
- Read: `pcm_view`, `pcm_buyer`, `pcm_po_approve`, `finance_audit` (owner implied).
- Buyer (PO create, NPI create): `pcm_buyer`.
- PO approve / NPI approve: `pcm_po_approve`.
- Consignment: `pcm_consignment` (plus all read roles).
- Comment/inbox write: same as read roles.

## Surfaces
- Contract-only surface. Implemented and deployed; see `/pcm/openapi.yaml` for the current surface definition.

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

| Method | Path | Request schema | Response schema |
| --- | --- | --- | --- |
| POST | /comment | CommentAddRequest | CommentEnvelope |
| POST | /comment/get | CommentGetRequest | CommentEnvelope |
| POST | /comment/list | CommentListRequest | CommentListEnvelope |
| POST | /comment/report | CommentReportRequest | CommentReportEnvelope |
| POST | /comment/revise | CommentReviseRequest | CommentEnvelope |
| POST | /comment/status | CommentStatusRequest | CommentEnvelope |
| POST | /consignment/get | ConsignmentGetRequest | ConsignmentEnvelope |
| POST | /consignment/list | ConsignmentListRequest | ConsignmentListEnvelope |
| POST | /consignment/settle | ConsignmentSettleRequest | ConsignmentEnvelope |
| POST | /credit/apply | CreditApplyRequest | RtvEnvelope |
| POST | /inbox/create | InboxCreateRequest | InboxEnvelope |
| POST | /inbox/get | InboxGetRequest | InboxEnvelope |
| POST | /inbox/list | InboxListRequest | InboxListEnvelope |
| POST | /inbox/state | InboxStateRequest | InboxEnvelope |
| POST | /inbox/status | InboxStatusRequest | InboxEnvelope |
| POST | /invoice/dispute | InvoiceDisputeRequest | InvoiceEnvelope |
| POST | /invoice/get | InvoiceGetRequest | InvoiceEnvelope |
| POST | /invoice/list | InvoiceListRequest | InvoiceListEnvelope |
| POST | /invoice/match | InvoiceMatchRequest | InvoiceEnvelope |
| POST | /invoice/resolve | InvoiceResolveRequest | InvoiceEnvelope |
| POST | /npi/approve | NpiApproveRequest | NpiEnvelope |
| POST | /npi/create | NpiCreateRequest | NpiEnvelope |
| POST | /npi/get | NpiGetRequest | NpiEnvelope |
| POST | /npi/list | NpiListRequest | NpiListEnvelope |
| POST | /npi/submit | NpiSubmitRequest | NpiEnvelope |
| POST | /po/approve | PoApproveRequest | PoEnvelope |
| POST | /po/create | PoCreateRequest | PoEnvelope |
| POST | /po/get | PoGetRequest | PoEnvelope |
| POST | /po/issue | PoIssueRequest | PoEnvelope |
| POST | /po/list | PoListRequest | PoListEnvelope |
| POST | /po/suggest | PoSuggestRequest | PoSuggestEnvelope |
| POST | /receipt/get | ReceiptGetRequest | ReceiptEnvelope |
| POST | /receipt/list | ReceiptListRequest | ReceiptListEnvelope |
| POST | /receipt/record | ReceiptRecordRequest | ReceiptEnvelope |
| POST | /po/status/recalculate | PoStatusRecalculateRequest | PoStatusRecalculateEnvelope |
| POST | /rtv/create | RtvCreateRequest | RtvEnvelope |
| POST | /rtv/get | RtvGetRequest | RtvEnvelope |
| POST | /rtv/list | RtvListRequest | RtvListEnvelope |
| POST | /rtv/receive | RtvReceiveRequest | RtvEnvelope |
| POST | /vendor/performance/list | VendorPerformanceListRequest | VendorPerformanceListEnvelope |
| POST | /vendor/scorecard/compute | VendorScorecardComputeRequest | VendorScorecardEnvelope |
| POST | /vendor/scorecard/get | VendorScorecardGetRequest | VendorScorecardEnvelope |
| POST | /vendor/scorecard/list | VendorScorecardListRequest | VendorScorecardListEnvelope |
| POST | /worksheet/create | WorksheetCreateRequest | WorksheetEnvelope |
| POST | /worksheet/get | WorksheetGetRequest | WorksheetEnvelope |
| POST | /worksheet/list | WorksheetListRequest | WorksheetListEnvelope |
| POST | /worksheet/submit | WorksheetSubmitRequest | WorksheetEnvelope |

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

## Example envelopes
Success envelope (shape-only):
```json
{
  "success": true,
  "data": { "example": "see schema for fields" },
  "stats": { "service": "pcm", "call": "pcm_example", "timestamp_utc": "2026-01-01T00:00:00Z", "build": { "build_major": "MONDAY", "build_minor": "0000000000", "build_id": "MONDAY-0000000000" } }
}
```

Error envelope (shape-only):
```json
{
  "success": false,
  "error": {
    "error_code": "pcm.validation_failed",
    "http_status": 400,
    "retryable": false,
    "major": { "tag": "validation-error", "message": { "en_US": "Invalid request." } }
  },
  "stats": { "service": "pcm", "call": "pcm_example", "timestamp_utc": "2026-01-01T00:00:00Z", "build": { "build_major": "MONDAY", "build_minor": "0000000000", "build_id": "MONDAY-0000000000" } }
}
```


## 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".
- **Receipt rejection reason codes**: `POST /receipt/record` validates `rejection_reason` on each line where `rejected_qty > 0`. The value must be a valid `RECEIVING.*` code from the shared reason code taxonomy (e.g. `RECEIVING.DAMAGED`, `RECEIVING.DEFECTIVE`). Unknown codes are rejected with `400 invalid-input`.
- **PO auto-transition on receipt**: `POST /receipt/record` with a `po_id` automatically transitions the PO status based on cumulative received qty vs ordered qty across all receipts. All lines fulfilled → PO becomes `received`; partial → `partially_received`. The response includes `po_status`. A `po-receipt-completed` event is emitted when PO reaches `received`. Receipt default status is `received` (not `recorded`).
- **RTV `from_bucket`**: each RTV line accepts an optional `from_bucket` field specifying which ICS stock bucket the returned quantity should be deducted from (e.g. `damaged`, `quarantine`, `available`). Defaults to `available`. On `rtv/receive`, PCM calls ICS `adjustment/create` (best-effort) for each line with a negative delta from the specified bucket.

## OpenAPI
- Contract schema: <a href="https://doc.g3nretailstack.com/pcm/openapi.yaml" target="_blank" rel="noopener noreferrer">https://doc.g3nretailstack.com/pcm/openapi.yaml</a>
- Comment/inbox list defaults: `status=current` / `status=inbox`; use `status=all` to include archived/doomed.


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