# CRM MCP Protocol

This file is published to <a href="https://mcp.g3nretailstack.com/crm/PROTOCOL.md" target="_blank" rel="noopener noreferrer">https://mcp.g3nretailstack.com/crm/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/crm`
- Health check: `GET /crm/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: `crm_view`, `crm_manage`, `crm_privacy_admin`, `crm_tax_exemption_admin`, `loyalty_admin`, `giftcard_admin`, `finance_audit` (owner implied).
- Customer write: `crm_manage`, `crm_privacy_admin`, `crm_edit`.
- Customer privacy: `crm_privacy_admin`, `crm_manage`.
- Fraud/hold: `crm_manage`, `crm_privacy_admin`.
- Loyalty: `loyalty_admin`, `crm_manage`.
- Gift card: `giftcard_admin`, `crm_manage`.
- Tax exemption: `crm_tax_exemption_admin`, `crm_manage`.
- Account write: `crm_manage`, `crm_privacy_admin`, `crm_edit`.
- Comment/inbox write: `crm_manage`, `crm_privacy_admin`, `crm_edit`.

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

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

| Method | Path | Request schema | Response schema |
| --- | --- | --- | --- |
| POST | /account/contact/create | AccountContactCreateRequest | AccountContactEnvelope |
| POST | /account/contact/get | AccountContactGetRequest | AccountContactEnvelope |
| POST | /account/contact/list | AccountContactListRequest | AccountContactListEnvelope |
| POST | /account/contact/status/set | AccountContactStatusSetRequest | AccountContactEnvelope |
| POST | /account/contact/update | AccountContactUpdateRequest | AccountContactEnvelope |
| POST | /account/create | AccountCreateRequest | AccountEnvelope |
| POST | /account/get | AccountGetRequest | AccountEnvelope |
| POST | /account/list | AccountListRequest | AccountListEnvelope |
| POST | /account/status/set | AccountStatusSetRequest | AccountEnvelope |
| POST | /account/update | AccountUpdateRequest | AccountEnvelope |
| POST | /account/credit/consume | AccountCreditConsumeRequest (inline) | AccountEnvelope |
| POST | /account/credit/release | AccountCreditReleaseRequest (inline) | AccountEnvelope |
| 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 | /customer/consent/get | CustomerConsentGetRequest | CustomerConsentEnvelope |
| POST | /customer/consent/set | CustomerConsentSetRequest | CustomerConsentEnvelope |
| POST | /customer/create | CustomerCreateRequest | CustomerEnvelope |
| POST | /customer/dedupe/list | CustomerDedupeRequest | CustomerDedupeEnvelope |
| POST | /customer/flag/list | CustomerFlagListRequest | CustomerFlagListEnvelope |
| POST | /customer/flag/set | CustomerFlagSetRequest | CustomerFlagEnvelope |
| POST | /customer/fraud/assess | CustomerFraudAssessRequest | CustomerFraudAssessEnvelope |
| POST | /customer/fraud/list | CustomerFraudListRequest | CustomerFraudListEnvelope |
| POST | /customer/get | CustomerGetRequest | CustomerEnvelope |
| POST | /customer/hold/list | CustomerHoldListRequest | CustomerHoldListEnvelope |
| POST | /customer/hold/resolve | CustomerHoldResolveRequest | CustomerHoldEnvelope |
| POST | /customer/list | CustomerListRequest | CustomerListEnvelope |
| POST | /customer/merge | CustomerMergeRequest | CustomerEnvelope |
| POST | /customer/merge/list | CustomerMergeListRequest | CustomerMergeListEnvelope |
| POST | /customer/search | CustomerSearchRequest | CustomerListEnvelope |
| POST | /customer/anonymize | CustomerAnonymizeRequest (inline) | CustomerEnvelope |
| POST | /customer/delete | CustomerDeleteRequest (inline) | Envelope |
| POST | /exemption/certificate/create | ExemptionCertificateCreateRequest | ExemptionCertificateEnvelope |
| POST | /exemption/certificate/get | ExemptionCertificateGetRequest | ExemptionCertificateEnvelope |
| POST | /exemption/certificate/list | ExemptionCertificateListRequest | ExemptionCertificateListEnvelope |
| POST | /exemption/certificate/status/set | ExemptionCertificateStatusSetRequest | ExemptionCertificateEnvelope |
| POST | /giftcard/issue | StoredValueIssueRequest | StoredValueEnvelope |
| POST | /giftcard/redeem | StoredValueRedeemRequest | StoredValueEnvelope |
| 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 | /loyalty/adjust | LoyaltyAdjustRequest | LoyaltyTxnEnvelope |
| POST | /loyalty/earn | LoyaltyEarnRequest | LoyaltyTxnEnvelope |
| POST | /loyalty/preview | LoyaltyPreviewRequest | LoyaltyPreviewEnvelope |
| POST | /loyalty/recalculate | LoyaltyRecalculateRequest | LoyaltyRecalculateEnvelope |
| POST | /loyalty/expire | LoyaltyExpireRequest | LoyaltyExpireEnvelope |
| POST | /loyalty/policy/get | LoyaltyPolicyGetRequest | LoyaltyPolicyEnvelope |
| POST | /loyalty/policy/set | LoyaltyPolicySetRequest | LoyaltyPolicyEnvelope |
| POST | /loyalty/redeem | LoyaltyRedeemRequest | LoyaltyTxnEnvelope |
| POST | /loyalty/restore | LoyaltyRestoreRequest | LoyaltyTxnEnvelope |
| POST | /loyalty/reverse | LoyaltyReverseRequest | LoyaltyTxnEnvelope |
| POST | /retention/policy/get | RetentionPolicyGetRequest (inline) | Envelope |
| POST | /retention/policy/set | RetentionPolicySetRequest (inline) | Envelope |
| POST | /subscription/create | SubscriptionCreateRequest | SubscriptionEnvelope |
| POST | /subscription/get | SubscriptionGetRequest | SubscriptionEnvelope |
| POST | /subscription/list | SubscriptionListRequest | SubscriptionListEnvelope |
| POST | /subscription/pause | SubscriptionLifecycleRequest | SubscriptionEnvelope |
| POST | /subscription/resume | SubscriptionLifecycleRequest | SubscriptionEnvelope |
| POST | /subscription/cancel | SubscriptionLifecycleRequest | SubscriptionEnvelope |
| POST | /warranty/register | WarrantyRegisterRequest (inline) | WarrantyEnvelope |
| POST | /warranty/get | WarrantyGetRequest (inline) | WarrantyEnvelope |
| POST | /warranty/list | WarrantyListRequest (inline) | WarrantyListEnvelope |
| POST | /warranty/status/set | WarrantyStatusSetRequest (inline) | WarrantyEnvelope |
| POST | /warranty/update | WarrantyUpdateRequest (inline) | WarrantyEnvelope |

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

### `loyalty-expire-sweep`
Scans customer records looking for loyalty balances with expired expiry buckets (`expires_at <= as_of`). For each match, zeroes out expired points and update the customer's loyalty balance. Cursor-based; resumes across invocations. Default scan limit: 50 (env `CRM_LOYALTY_EXPIRE_SWEEP_LIMIT`).

Request: `{ "job": "loyalty-expire-sweep", "as_of_utc?": string, "limit?": number, "dry_run?": boolean }`

Response shape:
```json
{ "job": "loyalty-expire-sweep", "run_id": "uuid", "as_of": "ISO", "limit": 50, "scanned": 0, "matched": 0, "processed": 0, "expired_customers": 0, "expired_points": 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.

## 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/crm/openapi.yaml" target="_blank" rel="noopener noreferrer">https://doc.g3nretailstack.com/crm/openapi.yaml</a>
- Customers: create/get/list/search, dedupe list, merge + merge list, consent get/set, flags, fraud/hold, anonymize, delete.
- B2B accounts: account create/get/list/status/set/update, credit consume/release, plus contact create/get/list/status/set/update.
- Tax exemption certificate surfaces include create/get/list and status set.
- Comments: comment add/get/list/revise/status/report.
- Inbox: inbox create/get/list/status/state.
- Subscriptions: create/get/list/pause/resume/cancel.
- Warranties: register/get/list/status/set/update.
- Retention policy: get/set.
- Loyalty: earn/redeem/adjust/reverse/restore/expire, policy get/set.


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