Appearance
Surfaces
Interactive API Explorer: Explorer
Base: https://api.g3nretailstack.com/pmc (custom domain; stage trimmed). OpenAPI: https://doc.g3nretailstack.com/pmc/openapi.yaml.
Responses use { success, data?, error?, build, stats? }. stats includes non-reversible session_fingerprint/api_key_fingerprint for correlation; session_guid is never returned.
Auth + tenancy
- Required:
x-orgcodeheader. - Auth:
x-session-guid(user session) orx-api-key(org-bound service account). For POSTs, you may also passsession_guidorapi_keyin the JSON body; GETs use headers. - Optional cost attribution:
x-cccodeheader (or bodycccode). Format^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$, canonicalized to uppercase; invalid values or header/body mismatch → HTTP 400. - Roles: read requires
pmc_view(owner implied; acceptspvv/viewer andpma/admin synonyms). Writes requirepmc_publish(owner implied; acceptspma/admin synonym).
Identifier policy
- Direct get/update/status calls require GUID/ID fields (
*_idor legacy*_guidwhere that is the canonical field name). Code-based lookups are resolve/search only. - Responses never include both
*_idand*_guidfor the same record (no dual-field output). - Exceptions (email-based UAS, PVM resolve, MRS
container+record_id) are listed in /common/ids-codes.html.
Surface Types (explicit)
API Gateway
- Status: Available.
- Base:
https://api.g3nretailstack.com/pmc - Notes: Primary tenant surface for PMC queries and publish workflows.
Direct Lambda
- Status: Not offered.
- Notes: No direct Lambda surface is documented for PMC.
CLI
- Status: Available.
- Command:
g3n pmc ...(API Gateway). - Notes: See
cli/README.md.
MCP
- Status: Available.
- Canonical protocol:
https://mcp.g3nretailstack.com/pmc/PROTOCOL.md - Mirror:
https://doc.g3nretailstack.com/pmc/PROTOCOL.md
Request builder (canonical)
Headers (canonical)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"
-H "x-cccode: $CCCODE" # optional
-H "content-type: application/json"Field placement
- GET: query string; auth/tenancy in headers.
- POST: JSON body; auth/tenancy in headers (body auth accepted for convenience).
GET template
bash
curl -sS -G "https://api.g3nretailstack.com/pmc/product/get" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "variant_id=VARIANT_ID" \
--data-urlencode "channel_guid=CHANNEL_GUID"POST template
bash
curl -sS -X POST "https://api.g3nretailstack.com/pmc/publish/run/start" \
-H "content-type: application/json" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
-d '{"style_id":"STYLE_ID","variant_id":"VARIANT_ID","channel_guid":"CHANNEL_GUID","reason":"initial publish"}'Field placement (GET vs POST)
- GET: parameters go in the query string; auth/tenancy in headers. Do not send JSON bodies on GET.
- POST: parameters go in the JSON body; auth/tenancy in headers (body auth is accepted for convenience, but headers are canonical).
Retry guidance (endpoint class)
- GET reads/lists/search are safe to retry with identical inputs.
- POST writes (publish runs, profile/pointer updates, pack lifecycle) should only be retried when you can tolerate duplicates or the endpoint uses
expected_revisionfor safe replay (refetch on 409).
Anti-enumeration (404)
Some org-scoped reads return 404 not-found when the caller is not associated with the org. This hides org existence from unrelated callers. Treat 404 as ambiguous (not found or not associated) and use the self-check flow in /common/troubleshooting.html.
Pagination: default limit=8, clamp 1–256, opaque JSON next_token cursors (echo verbatim).
Optimistic concurrency: revisioned records require expected_revision when updating existing state. Missing → HTTP 428 expected-revision-required; mismatch → HTTP 409 conflict with current record snapshot + current revision.
Core model + invariants
- Product identity:
variant_id × channel_guid(sales channel implies logical facility + market/locale). - Each publish creates an immutable revision with a required
reason. - Exactly one revision may be online at a time for a given product (or none).
online_state=offlinemeans no online pointer. - PMC is query-authoritative: list/search return PMC snapshots (no PVM resolver call during queries).
- Auto-offline: when upstream PVM containers become ineligible, PMC clears the online revision (minutes drift window).
Build
Canonical curl headers (API Gateway)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"GET /build/stats— build metadata for the running service.- Headers:
x-orgcodeandx-session-guidorx-api-key
- Headers:
Example:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/build/stats" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID"Product (serving)
Canonical curl headers (API Gateway)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"GET /product/get
Fetch by product_id, or by variant_id + channel_guid. Query:
product_idOR (variant_id+channel_guid) Headers:x-orgcodeandx-session-guidorx-api-keyReturns{ product, online_revision }where:product(head) includes identity (product_id/product_key/variant_id/style_id/brand/category/department/division), channel + locale (channel_guid,channel_code,market_code,logical_guid,locale_codes,default_locale_code), match keys (variant_code,sku,identifier_keys,alias_keys,barcode_values), and stamp metadata (pack_refs,taxonomy_version,biz_unit_code,region_code,country_code,online_rev,online_rev_id,online_reason_code,offline_reason_code,max_rev,last_publish_*, timestamps,revision).online_revisionis the current online revision (if any).
Examples:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/product/get" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "product_id=PROD123"
curl -sS -G "https://api.g3nretailstack.com/pmc/product/get" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "variant_id=VAR123" \
--data-urlencode "channel_guid=CHN123"GET /product/list
Query: one filter at a time, plus optional online_state, limit, next_token. Headers: x-orgcode and x-session-guid or x-api-key Exact-match filters only; one filter at a time (multiple filters → HTTP 400 invalid-input).
- Filters (one of):
- ID facets:
variant_id,style_id,brand_id,vendor_id,manufacturer_id,category_id,department_id,division_id,channel_guid,logical_guid - Code/locale facets:
style_code,category_code,department_code,division_code,channel_code,market_code,locale_code,default_locale_code - Match keys:
variant_code,sku,identifier_type + identifier_value,alias_tag + alias_value,barcode_value
- ID facets:
online_state=online|offline|both(defaultboth); legacyonly_online=truemaps toonline.- No filter → org-wide list (bucketed; no scans).
- Canonicalization: codes uppercased;
identifier_typelowercased;alias_valuewhitespace-collapsed then uppercased; locale codes trimmed; barcode requires digits-only (length 8/12/13/14). identifier_typeandidentifier_valuemust be provided together; same foralias_tag+alias_value.
Examples:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/product/list" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "style_id=STYLE123" \
--data-urlencode "online_state=online" \
--data-urlencode "limit=8"
curl -sS -G "https://api.g3nretailstack.com/pmc/product/list" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "sku=SKU-001" \
--data-urlencode "online_state=both"POST /product/group
Bounded group-by counts for a single dimension. Provide dimension and optional values[]; omit values to return top buckets (bounded by limit, default 50). When values[] is provided, PMC serves counts from the rollup table when available; otherwise it falls back to search-plane aggregations. Top buckets always use the search plane. For category/department/division group-by (IDs or codes), require channel_guid, channel_code, or logical_guid to bound the query.
online_state=online|offline|both(defaultboth).dimensionallowlist:variant_id,style_id,brand_id,vendor_id,manufacturer_id,category_id,department_id,division_id,channel_guid,logical_guid,style_code,category_code,department_code,division_code,channel_code,market_code,locale_code,default_locale_code,biz_unit_code,region_code,country_code.
Examples:
sh
curl -sS -X POST "https://api.g3nretailstack.com/pmc/product/group" \
-H "content-type: application/json" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
-d '{
"dimension": "category_id",
"values": ["CAT123"],
"online_state": "online",
"channel_guid": "CHN123"
}'GET /product/search
Partial-match search (prefix/contains/regex) for operational workflows. Results are PMC product heads.
- Required:
q. - Optional:
field=any|sku|code|barcode|identifier|alias,mode=prefix|contains|regex(defaultcontains),online_state,channel_guid,logical_guid. - Guardrails:
containsrequiresqlength ≥ 3;prefix≥ 2.field=barcoderequires digits-onlyqlength ≥ 4.regexrequirespmc_publish(or owner) and is applied to normalized lowercase text. Headers:x-orgcodeandx-session-guidorx-api-key
Examples:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/product/search" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "q=SHOE" \
--data-urlencode "field=code" \
--data-urlencode "mode=prefix"
curl -sS -G "https://api.g3nretailstack.com/pmc/product/search" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "q=012345678905" \
--data-urlencode "field=barcode" \
--data-urlencode "mode=contains"GET /product/revision/list
List revision history for a product (paged). Filters: product_id or variant_id + channel_guid, optional online_state. Headers: x-orgcode and x-session-guid or x-api-key
Examples:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/product/revision/list" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "product_id=PROD123" \
--data-urlencode "online_state=both" \
--data-urlencode "limit=8"Online pointer control
Canonical curl headers (API Gateway)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"
-H "content-type: application/json"POST /product/online/set— set which revision is online. Requiresexpected_revision, plusrev+rev_id. Optionalreason_code. Identify the product byproduct_idorvariant_id + channel_guid.POST /product/online/clear— clear the online pointer (no revision online). Requiresexpected_revision. Optionalreason_code.
Pointer kinds (staged/preview/fallback)
Canonical curl headers (API Gateway)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"
-H "content-type: application/json"GET /product/pointer/get—pointer_kindrequired; returns pointer ornull.- Query:
pointer_kind, plusproduct_idor (variant_id+channel_guid) - Headers:
x-orgcodeandx-session-guidorx-api-key
- Query:
- Example:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/product/pointer/get" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "pointer_kind=staged" \
--data-urlencode "product_id=PROD123"POST /product/pointer/set—pointer_kind,rev,rev_idrequired; optionalreason,effective_at,expires_at(ISO-8601). Useexpected_revisionwhen updating an existing pointer.POST /product/pointer/clear—pointer_kind+expected_revision.
Content packs
Packs are immutable, versioned bundles referenced by pack_refs in publish runs.
Canonical curl headers (API Gateway)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"
-H "content-type: application/json"POST /pack/create— create a pack.- Inline JSON:
content_type=application/jsonwithpayloadprovided; service computes MD5. Max size 25 MB. - Presigned upload: provide
size_bytes+content_md5(hex). Returnspresign.upload_url+upload_token; pack statuspending_upload. pack_kindis required and must match[a-z0-9_-]{2,32};pack_idoptional (if provided,pack_versionis required; otherwisepack_versiondefaults to 1).
- Inline JSON:
POST /pack/complete— finalize presigned upload. Requirespack_id,pack_version,upload_token,expected_revision. Validates size, marks packactive.GET /pack/get— fetch a pack bypack_id + pack_version.- Query:
pack_id,pack_version - Headers:
x-orgcodeandx-session-guidorx-api-key
- Query:
GET /pack/list— list versions for apack_id(paged).- Query:
pack_id, optionallimit,next_token - Headers:
x-orgcodeandx-session-guidorx-api-key
- Query:
Examples:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/pack/get" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "pack_id=PACK123" \
--data-urlencode "pack_version=1"
curl -sS -G "https://api.g3nretailstack.com/pmc/pack/list" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "pack_id=PACK123" \
--data-urlencode "limit=8"Publishability profiles
Canonical curl headers (API Gateway)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"
-H "content-type: application/json"GET /publish-profile/get— fetch profile bychannel_code.- Query:
channel_code - Headers:
x-orgcodeandx-session-guidorx-api-key
- Query:
POST /publish-profile/set— create/update profile. Update requiresexpected_revision.- Fields:
required_sku(bool),required_identifier_types[],required_alias_tags[].
- Fields:
Example:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/publish-profile/get" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "channel_code=WEB"Publish runs (client-driven)
Publish orchestration is client-driven; calling start does not publish by itself.
Canonical curl headers (API Gateway)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"
-H "content-type: application/json"POST /publish/run/start— start a run.- Required:
reason. confirm_all=trueis required to publish all variants (no selector).- Primary selector (choose one):
variant_id(requiresstyle_id),style_id,brand_id,vendor_id,manufacturer_id,category_id. - Optional secondary filters:
control_type,service_flag,kit_flag. - Optional channel scope:
logical_guid,channel_guid,channel_code,market_code(run will fail if no active channels match). - Optional stamps:
taxonomy_version,biz_unit_code,region_code,country_code(each must match[A-Z0-9_-]{2,64}). - Optional
pack_refs(max 8); each ref must point to an active pack, andpack_kind(if provided) must match.
- Required:
POST /publish/run/step— process the next page. Requiresrun_id+expected_revision; optionallimit(default 8). Updates counts (variants_processed,publish_ok,publish_skipped,skipped_reasons) and appends manifest parts.GET /publish/run/get— fetch run status and counters.- Query:
run_id - Headers:
x-orgcodeandx-session-guidorx-api-key
- Query:
POST /publish/run/cancel— cancel a running run (expected_revisionrequired).
Manifests
GET /publish/run/manifest/list— list manifest parts (part number, hash, size, schema version).- Query:
run_id, optionallimit,next_token - Headers:
x-orgcodeandx-session-guidorx-api-key
- Query:
GET /publish/run/manifest/presign— presign a manifest part download.- Query:
run_id,part_number - Headers:
x-orgcodeandx-session-guidorx-api-key
- Query:
- Manifest content is NDJSON (gzip). Some HTTP clients auto-decompress; accept either.
Examples:
sh
curl -sS -G "https://api.g3nretailstack.com/pmc/publish/run/get" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "run_id=RUN123"
curl -sS -G "https://api.g3nretailstack.com/pmc/publish/run/manifest/list" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "run_id=RUN123" \
--data-urlencode "limit=8"
curl -sS -G "https://api.g3nretailstack.com/pmc/publish/run/manifest/presign" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "run_id=RUN123" \
--data-urlencode "part_number=1"Error tags (representative)
invalid-input, forbidden-role, unauthorized, not-found, conflict, expected-revision-required, invalid-state, upstream-failed, internal-error.
CLI (API Gateway)
PMC CLI commands mirror core API surfaces and require --orgcode + --session-guid. Base defaults to https://api.g3nretailstack.com/pmc.
Examples:
g3n pmc product-get --product-id <id> --orgcode ORG --session-guid ...g3n pmc product-list --style-id <id> --online-state online --limit 8 --orgcode ORG --session-guid ...g3n pmc product-list-by-identifier --type sku --value SKU123 --orgcode ORG --session-guid ...g3n pmc product-search --q ABC --mode contains --orgcode ORG --session-guid ...g3n pmc product-revision-list --product-id <id> --orgcode ORG --session-guid ...g3n pmc product-online-set --product-id <id> --expected-revision <rev> --rev 2 --rev-id <rev_id> --reason-code SYSTEM.MANUAL_ROLLBACK --orgcode ORG --session-guid ...g3n pmc publish-profile-set --channel-code SHOPIFY --required-sku true --required-identifier-types '["sku"]' --orgcode ORG --session-guid ...g3n pmc publish-run-start --reason "weekly" --channel-code SHOPIFY --orgcode ORG --session-guid ...g3n pmc publish-run-step --run-id <id> --expected-revision <rev> --orgcode ORG --session-guid ...
Service-account API keys can call the API directly via x-api-key headers; the CLI currently uses sessions.
Error envelope example (canonical)
json
{
"success": false,
"error": {
"error_code": "pmc.conflict_revision",
"http_status": 409,
"retryable": false,
"request_id": "req-123",
"trace_id": "trace-abc",
"major": { "tag": "conflict", "message": { "en_US": "Expected revision does not match the current record." } },
"details": { "expected_revision": "3", "current_revision": "4" },
"conflict_snapshot": { "revision": 4 }
},
"build": { "...": "..." },
"stats": { "call": "example", "service": "pmc", "timestamp_utc": "2026-01-21T00:00:00Z", "request_id": "req-123" }
}Role requirements (by endpoint family)
- Read/list/search:
pmc_view(aliases:pvv) or owner. - Publish runs + online pointer:
pmc_publish(alias:pma) or owner.
Idempotency & retries
- Publish run
startis not idempotent; reuse run id if you retry after timeout. - Run
stepand pointer updates requireexpected_revision; re-read on409/428before retrying.
Common pitfalls
- Missing
channel_codeorlogical_guidin queries yields empty results. online_statedefaults toboth; set explicitly foronline/offlineviews.
Examples (core families)
Publish run start
json
{ "reason": "weekly publish", "channel_code": "WEB", "style_id": "STYLE_ID" }Response (shape):
json
{ "success": true, "data": { "run_id": "RUN_ID", "revision": "RUN_REV" }, "build": { "...": "..." }, "stats": { "...": "..." } }Product get
json
{ "product_id": "PRODUCT_ID" }Response (shape):
json
{ "success": true, "data": { "product": { "product_id": "PRODUCT_ID" } }, "build": { "...": "..." }, "stats": { "...": "..." } }