Appearance
Surfaces
Interactive API Explorer: Explorer
The authoritative surface is the OpenAPI spec:
/ipm/openapi.yaml
Additional surfaces are available: MCP (/ipm/mcp.html) and CLI (g3n ipm ...; see cli/README.md).
Surface Types (explicit)
API Gateway
- Status: Available.
- Base:
https://api.g3nretailstack.com/ipm - Notes: Primary tenant surface for integrations (webhooks, CDC, bulk jobs, KPI).
Direct Lambda
- Status: Not offered.
- Notes: No direct Lambda surface is documented for IPM.
CLI
- Status: Available.
- Command:
g3n ipm ...(API Gateway). - Notes: See
cli/README.md.
MCP
- Status: Available.
- Canonical protocol:
https://mcp.g3nretailstack.com/ipm/PROTOCOL.md - Mirror:
https://doc.g3nretailstack.com/ipm/PROTOCOL.md
Auth + tenancy
- Auth headers:
x-session-guid(user session) orx-api-key(org-bound service account) are required for all API Gateway calls, including/stat. - Tenant endpoints require
x-orgcode(health checks do not). - Header auth is canonical; body auth is accepted where documented. See /common/headers-identity.html.
- Optional cost attribution:
x-cccode(or request fieldcccode) where supported; see OpenAPI. - Non-associated callers receive
404 not-found(anti-enumeration).
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.
Request builder (API Gateway)
Headers (canonical)
bash
-H "x-orgcode: $ORGCODE"
-H "x-session-guid: $SESSION_GUID" # or: -H "x-api-key: $API_KEY"
-H "content-type: application/json"Template
bash
curl -sS -X POST "https://api.g3nretailstack.com/ipm/<endpoint>" \
-H "content-type: application/json" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
-d '{"...": "..."}'Notes
- Replace
<endpoint>with a route from the OpenAPI inventory. - Health checks may omit
x-orgcode; tenant routes require it. - Header auth is canonical; body
session_guid/api_keyis accepted where documented.
Endpoint inventory (OpenAPI parity)
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) |
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), orreplay(manual replay).- The event payload includes its own
reason(why the event occurred);delivery_reasonexplains why this delivery attempt was sent.
Webhook secrets & rotation
signing_secretis returned once on webhook create;get/listonly returnsecret_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 to avoid downtime.
- If a dedicated rotate endpoint is introduced, it will return the new secret once and enforce an overlap window (UNCONFIRMED).
- Treat webhook secrets as credentials; store in a secrets manager and rotate immediately on exposure.
Error tags
Common tags (see /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" },
"build": { "build_major": "MONDAY", "build_minor": "0000000000", "build_id": "MONDAY-0000000000" },
"stats": { "call": "example", "service": "ipm", "timestamp_utc": "2026-01-24T00:00:00Z" }
}Error envelope (shape-only):
json
{
"success": false,
"error": {
"error_code": "ipm.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": "rev-3", "current_revision": "rev-4" },
"conflict_snapshot": { "revision": "rev-4" }
},
"build": { "...": "..." },
"stats": { "call": "example", "service": "ipm", "timestamp_utc": "2026-01-24T00:00:00Z", "request_id": "req-123" }
}Export contract (event stream alignment)
POST /ipm/export/contract/list- Optional filters:
service,limit,next_token. - Returns per-service contract metadata (stream, format, partitioning, schema URL, actions).
Example request:
json
{
"service": "scm",
"limit": 25
}KPI snapshots
POST /ipm/kpi/record— record a KPI snapshot (write).POST /ipm/kpi/query— query KPI snapshots by bucket and time window (read).POST /ipm/kpi/alert— record a KPI alert and optionally deliver to inbox.
Example record:
json
{
"request_context": { "orgcode": "ORG1", "actor": "user:U1", "context_source": "session" },
"reason": "kpi-rollup",
"source_refs": [{ "kind": "job", "id": "job_123" }],
"kpi": {
"kpi_code": "inventory_stockout_rate",
"scope": "facility",
"logical_guid": "LOG1",
"bucket": "daily",
"bucket_start": "2026-01-25T00:00:00Z",
"bucket_end": "2026-01-26T00:00:00Z",
"value": 0.034,
"value_unit": "ratio",
"dimensions": { "channel_code": "pos" }
}
}Role requirements (by endpoint family)
- Read/list:
integration_view(or owner). - Webhook/CDC/bulk management:
integration_admin(or owner).
Idempotency & retries
- Job creation is not idempotent unless documented; use caller-provided IDs where supported.
- Status updates require
expected_revisionwhen present.
Common pitfalls
- CDC cursors are opaque; persist and replay from stored cursors.
- Webhook verification tokens expire; verify promptly.
Examples (core families)
Webhook register
json
{ "webhook": { "target_url": "https://example.com/webhook", "filters": [{ "service": "scm", "action_prefix": "order-" }] }, "reason": "integration" }Response (shape):
json
{ "success": true, "data": { "webhook_id": "WH_ID" }, "build": { "...": "..." }, "stats": { "...": "..." } }CDC list (cursor)
json
{ "cursor": "CURSOR", "limit": 100, "reason": "sync" }Response (shape):
json
{ "success": true, "data": { "items": [], "next_cursor": "NEXT" }, "build": { "...": "..." }, "stats": { "...": "..." } }Example query:
json
{
"kpi_code": "inventory_stockout_rate",
"scope": "facility",
"logical_guid": "LOG1",
"bucket": "daily",
"from_utc": "2026-01-01T00:00:00Z",
"to_utc": "2026-01-31T00:00:00Z",
"limit": 30
}Example alert:
json
{
"request_context": { "orgcode": "ORG1", "actor": "user:U1", "context_source": "session" },
"reason": "threshold-breach",
"source_refs": [{ "kind": "kpi_snapshot", "id": "kpi_123" }],
"alert": {
"kpi_code": "inventory_stockout_rate",
"value": 0.12,
"threshold": { "comparator": "gt", "value": 0.1 },
"scope": "facility",
"logical_guid": "LOG1",
"bucket": "daily",
"bucket_start": "2026-01-25T00:00:00Z",
"bucket_end": "2026-01-26T00:00:00Z",
"team_guid": "TEAM1",
"inbox_service": "scm",
"priority": "high"
}
}