# IPM MCP Protocol

This file is published to <a href="https://mcp.g3nretailstack.com/ipm/PROTOCOL.md" target="_blank" rel="noopener noreferrer">https://mcp.g3nretailstack.com/ipm/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/ipm`
- Health check: `GET /ipm/stat` — public, no auth required.

## MCP transport & resources
- Transport/auth/options: see <a href="https://doc.g3nretailstack.com/common/mcp.html" target="_blank" rel="noopener noreferrer">/common/mcp.html</a>.
- MCP resources include protocol docs, OpenAPI contracts, and doc pages (surfaces/calls/playbooks).
- Streaming: JSON only today; SSE is not enabled on API Gateway (streaming would use a dedicated endpoint).

## 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).
- Auth headers (`x-session-guid` or `x-api-key`) are required for all API Gateway calls, including `/stat`.
- Tenant endpoints (non-`/stat`) require `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.

## Roles
- Read: `integration_view`, `integration_admin` (owner implied).
- Write: `integration_admin`.
- Read operations: event/catalog/list, export/contract/list, kpi/query, lifecycle/list, specimen/get, pagination/get, webhook/get, webhook/list, cdc/list, bulk/job/get, bulk/job/list.
- Write operations: kpi/record, kpi/alert, webhook/create, webhook/status/set, webhook/replay, bulk/import/create, bulk/import/continue, bulk/import/commit, bulk/export/create, bulk/export/continue.

## Surfaces
- Event catalog: `/event/catalog/list`
- Export contracts: `/export/contract/list`
- Lifecycle/state machines: `/lifecycle/list`
- Specimen payloads: `/specimen/get`
- Pagination conventions: `/pagination/get`
- Webhooks: `/webhook/*` (including `/webhook/replay`)
- CDC feed: `/cdc/list` (supports service/channel/facility/time filters)
- Bulk jobs: `/bulk/*` (includes `/bulk/import/continue` and `/bulk/export/continue`)
- KPI snapshots + alerts: `/kpi/query`, `/kpi/record`, `/kpi/alert`

## Webhook health
- Webhook records include `consecutive_failures`, `last_success_at`, `last_failure_at`, and `last_error` for monitoring.
- Webhooks auto-pause after repeated delivery failures (see `status` + `status_reason`).

## Webhook delivery payload
Webhook POST body:
```json
{
  "delivered_at": "2026-02-01T00:00:00Z",
  "webhook_id": "whk-123",
  "delivery_reason": "live",
  "event": { "event_id": "evt-123", "reason": "status-set", "service": "pvm", "action": "variant-updated" }
}
```

- `delivery_reason`: `live` (normal delivery), `retry` (automatic retry), or `replay` (manual replay).
- The event payload includes its own `reason` (why the event occurred); `delivery_reason` explains why this delivery attempt was sent.

## Webhook secrets & rotation
- `signing_secret` is returned **once** on webhook create; `get/list` return only `secret_last4`.
- Secrets are stored encrypted at rest (DynamoDB SSE) and never emitted in logs/events.
- Rotation posture (current): create a **new webhook**, verify delivery, then revoke the old webhook.
- If a rotate endpoint is introduced, it will return the new secret once and enforce an overlap window (UNCONFIRMED).

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

| Method | Path | Request schema | Response schema |
| --- | --- | --- | --- |
| POST | /bulk/export/continue | BulkExportContinueRequest | Envelope + data.job (BulkJob) |
| POST | /bulk/export/create | BulkExportCreateRequest | Envelope + data.job (BulkJob) |
| POST | /bulk/import/commit | BulkImportCommitRequest | Envelope + data.job (BulkJob) |
| POST | /bulk/import/continue | BulkImportContinueRequest | Envelope + data.job (BulkJob) |
| POST | /bulk/import/create | BulkImportCreateRequest | Envelope + data.job (BulkJob) + upload_url |
| POST | /bulk/job/get | BulkJobGetRequest | Envelope + data.job (BulkJob) + download_url/report_url |
| POST | /bulk/job/list | BulkJobListRequest | Envelope + data.items (BulkJob[]) |
| POST | /cdc/list | CdcListRequest | Envelope + data.items + next_token |
| POST | /event/catalog/list | EventCatalogListRequest | Envelope + data.items + next_token |
| POST | /export/contract/list | ExportContractListRequest | Envelope + data.contracts (ExportContract[]) + next_token |
| POST | /kpi/alert | KpiAlertRequest | Envelope + data.alert (KpiAlert) |
| POST | /kpi/query | KpiQueryRequest | Envelope + data.series (KpiSnapshot[]) + next_token |
| POST | /kpi/record | KpiRecordRequest | Envelope + data.kpi (KpiSnapshot) |
| POST | /lifecycle/list | LifecycleListRequest | Envelope + data.items + version |
| POST | /pagination/get | PaginationGetRequest | Envelope |
| POST | /specimen/get | SpecimenGetRequest | Envelope + data (specimen) |
| POST | /webhook/create | WebhookCreateRequest | Envelope + data.webhook (Webhook) + signing_secret |
| POST | /webhook/get | WebhookGetRequest | Envelope + data.webhook (Webhook) |
| POST | /webhook/list | WebhookListRequest | Envelope + data.items (Webhook[]) |
| POST | /webhook/replay | WebhookReplayRequest | Envelope + data (webhook_id, delivered, failed) |
| POST | /webhook/status/set | WebhookStatusSetRequest | Envelope + data.webhook (Webhook) |

## 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": "ipm", "call": "ipm_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": "ipm.validation_failed",
    "http_status": 400,
    "retryable": false,
    "major": { "tag": "validation-error", "message": { "en_US": "Invalid request." } }
  },
  "stats": { "service": "ipm", "call": "ipm_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".

## OpenAPI
- `https://doc.g3nretailstack.com/ipm/openapi.yaml`


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