# ICS MCP Protocol

This file is published to <a href="https://mcp.g3nretailstack.com/ics/PROTOCOL.md" target="_blank" rel="noopener noreferrer">https://mcp.g3nretailstack.com/ics/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/ics`
- Health check: `GET /ics/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: `ics_view`, `ics_operator`, `ics_planner`, `ics_adjust`, `ics_count`, `ics_transfer_approve`, `ics_cost_admin` (owner implied).
- Operator (receive/putaway/pick/pack/ship): `ics_operator`.
- Planner (replenishment): `ics_planner`, `ics_operator`.
- Count: `ics_count`, `ics_operator`.
- Adjust: `ics_adjust`, `ics_operator`.
- Transfer approve: `ics_transfer_approve`.
- Comment/inbox write: same as read roles.

## Surfaces
- Contract-only surface. Implemented and deployed; see `/ics/openapi.yaml` for the current surface definition.
- Key endpoints:
  - `POST /ics/receive/record` + `/ics/receive/get` + `/ics/receive/list`
  - `POST /ics/qc/complete`
  - `POST /ics/putaway/record` + `/ics/pick/record` + `/ics/pack/record` + `/ics/ship/record`
  - `POST /ics/replenishment/record` + `/ics/replenishment/suggest`
  - `POST /ics/reservation/create` + `/ics/reservation/release` + `/ics/reservation/expire`
  - `POST /ics/allocation/create` + `/ics/allocation/release` + `/ics/allocation/expire`
  - `POST /ics/stock/get` + `/ics/stock/list` + `/ics/stock/policy/get` + `/ics/stock/policy/set`
  - `POST /ics/stock/transition`
  - `POST /ics/count/plan/set|get|list|status`
  - `POST /ics/count/create|submit|approve|reject`
  - `POST /ics/comment|comment/get|comment/list|comment/revise|comment/status|comment/report`
  - `POST /ics/inbox/create|get|list|status|state`
  - `POST /ics/commit/create-direct` (direct commit without prior reservation)
  - `POST /ics/serial/register|get|lookup|lookup-udi|list|update|360` (serial/batch tracking)
  - `POST /ics/holdback/set|get|list` (holdback management)
  - `POST /ics/atp/query` (available-to-promise)
  - `POST /ics/demand/signal/get|list|compute` (demand signals)
  - `POST /ics/snapshot/create|get|list|detail` (stock snapshots)
  - `POST /ics/search/comments` + `POST /ics/search/inbox` (org-wide search across ICS/SCM/PCM/CRM/PPM/Influencer/Accounting)

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

| Method | Path | Request schema | Response schema |
| --- | --- | --- | --- |
| GET | /stat | — | StatEnvelope |
| POST | /adjustment/create | AdjustmentCreateRequest | AdjustmentEnvelope |
| POST | /allocation/create | AllocationCreateRequest | AllocationEnvelope |
| POST | /allocation/expire | AllocationExpireRequest | AllocationEnvelope |
| POST | /allocation/release | AllocationReleaseRequest | AllocationEnvelope |
| POST | /allocation/suggest | AllocationSuggestRequest | AllocationSuggestEnvelope |
| POST | /bin/create | BinCreateRequest | BinEnvelope |
| POST | /bin/get | BinGetRequest | BinEnvelope |
| POST | /bin/list | BinListRequest | BinListEnvelope |
| POST | /bin/status/set | BinStatusSetRequest | BinEnvelope |
| 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 | /commit/create | CommitCreateRequest | CommitEnvelope |
| POST | /commit/release | CommitReleaseRequest | CommitEnvelope |
| POST | /consignment/consume | ConsignmentConsumeRequest | ConsignmentEnvelope |
| POST | /consignment/receive | ConsignmentReceiveRequest | ConsignmentEnvelope |
| POST | /consignment/settle | ConsignmentSettlementRequest | ConsignmentEnvelope |
| POST | /count/approve | CountApproveRequest | CountEnvelope |
| POST | /count/create | CountCreateRequest | CountEnvelope |
| POST | /count/plan/get | CountPlanGetRequest | CountPlanEnvelope |
| POST | /count/plan/list | CountPlanListRequest | CountPlanListEnvelope |
| POST | /count/plan/set | CountPlanSetRequest | CountPlanEnvelope |
| POST | /count/plan/status/set | CountPlanStatusSetRequest | CountPlanEnvelope |
| POST | /count/reject | CountRejectRequest | CountEnvelope |
| POST | /count/submit | CountSubmitRequest | CountEnvelope |
| 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 | /pack/record | PackRecordRequest | StockCardEntryEnvelope |
| POST | /pick/record | PickRecordRequest | StockCardEntryEnvelope |
| POST | /putaway/record | PutawayRecordRequest | StockCardEntryEnvelope |
| POST | /qc/complete | QcCompleteRequest | ReceiveEnvelope |
| POST | /receive/get | ReceiveGetRequest | ReceiveEnvelope |
| POST | /receive/list | ReceiveListRequest | ReceiveListEnvelope |
| POST | /receive/record | ReceiveRecordRequest | ReceiveEnvelope |
| POST | /replenishment/record | ReplenishmentRecordRequest | StockCardEntryEnvelope |
| POST | /replenishment/rule/get | ReplenishmentRuleGetRequest | ReplenishmentRuleEnvelope |
| POST | /replenishment/rule/list | ReplenishmentRuleListRequest | ReplenishmentRuleListEnvelope |
| POST | /replenishment/rule/set | ReplenishmentRuleSetRequest | ReplenishmentRuleEnvelope |
| POST | /replenishment/rule/status/set | ReplenishmentRuleStatusSetRequest | ReplenishmentRuleEnvelope |
| POST | /replenishment/suggest | ReplenishmentSuggestRequest | ReplenishmentSuggestEnvelope |
| POST | /reservation/create | ReservationCreateRequest | ReservationEnvelope |
| POST | /reservation/expire | ReservationExpireRequest | ReservationEnvelope |
| POST | /reservation/release | ReservationReleaseRequest | ReservationEnvelope |
| POST | /stock/card/list | StockCardListRequest | StockCardEnvelope |
| POST | /stock/get | StockGetRequest | StockEnvelope |
| POST | /stock/list | StockListRequest | StockListEnvelope |
| POST | /stock/policy/get | StockPolicyGetRequest | StockPolicyEnvelope |
| POST | /stock/policy/set | StockPolicySetRequest | StockPolicyEnvelope |
| POST | /stock/transition | StockTransitionRequest | StockTransitionEnvelope |
| POST | /transfer/report | TransferReportRequest | TransferReportEnvelope |
| POST | /transfer/request/approve | TransferRequestApproveRequest | TransferEnvelope |
| POST | /transfer/request/create | TransferRequestCreateRequest | TransferEnvelope |
| POST | /transfer/request/get | TransferRequestGetRequest | TransferEnvelope |
| POST | /transfer/request/list | TransferRequestListRequest | TransferListEnvelope |
| POST | /transfer/request/submit | TransferRequestSubmitRequest | TransferEnvelope |
| POST | /transfer/shipment/get | TransferShipmentGetRequest | TransferShipmentEnvelope |
| POST | /transfer/shipment/list | TransferShipmentListRequest | TransferShipmentListEnvelope |
| POST | /transfer/shipment/receive | TransferReceiveRequest | TransferShipmentEnvelope |
| POST | /transfer/shipment/record | TransferShipmentRecordRequest | TransferShipmentEnvelope |
| POST | /transfer/suggest | TransferSuggestRequest | TransferSuggestEnvelope |
| POST | /search/comments | CommentSearchRequest | CommentSearchEnvelope |
| POST | /search/inbox | InboxSearchRequest | InboxSearchEnvelope |
| POST | /ship/record | ShipRecordRequest | StockCardEntryEnvelope |
| POST | /zone/create | ZoneCreateRequest | ZoneEnvelope |
| POST | /zone/get | ZoneGetRequest | ZoneEnvelope |
| POST | /zone/list | ZoneListRequest | ZoneListEnvelope |
| POST | /zone/status/set | ZoneStatusSetRequest | ZoneEnvelope |
| POST | /commit/create-direct | CommitCreateRequest | CommitEnvelope |
| POST | /serial/register | (inline) | SerialEnvelope |
| POST | /serial/get | (inline) | SerialEnvelope |
| POST | /serial/lookup | (inline) | SerialListEnvelope |
| POST | /serial/lookup-udi | (inline) | SerialListEnvelope |
| POST | /serial/list | (inline) | SerialListEnvelope |
| POST | /serial/update | (inline) | SerialEnvelope |
| POST | /serial/360 | (inline) | SerialEnvelope |
| POST | /holdback/set | (inline) | HoldbackEnvelope |
| POST | /holdback/get | (inline) | HoldbackEnvelope |
| POST | /holdback/list | (inline) | HoldbackListEnvelope |
| POST | /atp/query | (inline) | AtpQueryEnvelope |
| POST | /demand/signal/get | (inline) | DemandSignalEnvelope |
| POST | /demand/signal/list | (inline) | DemandSignalListEnvelope |
| POST | /demand/signal/compute | (inline) | DemandSignalEnvelope |
| POST | /snapshot/create | (inline) | SnapshotEnvelope |
| POST | /snapshot/get | (inline) | SnapshotEnvelope |
| POST | /snapshot/list | (inline) | SnapshotListEnvelope |
| POST | /snapshot/detail | (inline) | SnapshotDetailEnvelope |

## 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": "ics", "call": "ics_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": "ics.validation_failed",
    "http_status": 400,
    "retryable": false,
    "major": { "tag": "validation-error", "message": { "en_US": "Invalid request." } }
  },
  "stats": { "service": "ics", "call": "ics_example", "timestamp_utc": "2026-01-01T00:00:00Z", "build": { "build_major": "MONDAY", "build_minor": "0000000000", "build_id": "MONDAY-0000000000" } }
}
```

## Internal jobs (operator-only)
These jobs run via EventBridge-scheduled Lambda invocations (not API Gateway). The entry Lambda dispatches on `{ "source": "aws.events", "job": "<name>" }`.

### `replenishment-sweep`
Scans active `replenishment_rule` records. For each rule where the pick-zone stock is below `min_qty`, computes a recommended replenishment quantity (up to `max_qty`) and moves stock from the overstock zone/bin to the pick zone/bin. Idempotency key includes the rule ID and a time-window key (configurable via `ICS_REPLENISHMENT_SWEEP_WINDOW_MINUTES`, default 15 min). Cursor-based; resumes across invocations. Default scan limit: 50 (env `ICS_REPLENISHMENT_SWEEP_LIMIT`). Optional `orgcode` and `logical_guid` filters scope the scan.

Request: `{ "job": "replenishment-sweep", "as_of_utc?": string, "limit?": number, "dry_run?": boolean, "orgcode?": string, "logical_guid?": string }`

Response shape:
```json
{ "job": "replenishment-sweep", "run_id": "uuid", "as_of": "ISO", "limit": 50, "scanned": 0, "matched": 0, "processed": 0, "executed": 0, "skipped_missing_overstock": 0, "skipped_no_stock": 0, "conflicts": 0, "errors": 0, "next_cursor": null }
```


## 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.

### Idempotency keys
The following ICS endpoints accept an optional `idempotency_key` field for safe retry:
- `POST /ics/reservation/create`
- `POST /ics/bin/create`
- `POST /ics/zone/create`
- `POST /ics/stock/policy/set`

When provided, the key is scoped to the org and deduplicated within a rolling time window (default 15 min). Retrying with the same key within the window returns the original response without re-executing the mutation.

## 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.
- **`from_bucket` on adjustments**: optional field on `adjustment/create`. When set, deducts from the named bucket (e.g. `damaged`, `quarantine`, `consignment`) instead of `available`. Valid values: `available`, `quarantine`, `damaged`, `consignment`, `reserved`, `committed`, `in_transit`. Defaults to `available` if omitted.
- **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/ics/openapi.yaml" target="_blank" rel="noopener noreferrer">https://doc.g3nretailstack.com/ics/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)_
