Skip to content

Minimum Viable Flow (Login -> Org -> Product -> Publish)

This walkthrough is a copy-paste minimal flow that exercises the core services end-to-end: login -> org setup (OFM) -> product creation (PVM) -> channel publish (PMC).

All POST requests use JSON bodies; all GET requests use query parameters. Auth is carried in headers for org-scoped services, with body-auth exceptions for USM and UTL. Replace placeholders as needed.

Operator-only note: invitations and org verification are IAM direct Lambda steps (not API Gateway). You cannot complete this flow end-to-end without operator access for those steps. See User Provisioning for the complete operator setup flow (user creation, email verification, invitation minting).

Downloadable collections (Postman + Insomnia)

Import a collection and run the same flow in order:

Notes:

  • The org verification step is operator-only (direct Lambda / IAM); the collection includes a placeholder item with instructions.
  • Both collections use variables like API_BASE, SESSION_GUID, ORGCODE, and ORG_GUID. You can edit them manually; the Postman collection also includes tests to populate variables from responses.

0) Set placeholders

sh
export API_BASE="https://api.g3nretailstack.com"
export EMAIL="user@example.com"
export PASSCODE="Passw0rd!123"
export ORGCODE="ACME"
export INVITE_CODE="INV123"

1) Login (USM session)

Auth Placement

USM uses body authsession_guid and credentials live in the JSON body, not headers. All downstream org-scoped services (OFM, PVM, PMC, etc.) use header auth (x-session-guid). UTL also uses body auth. See /common/headers-identity.html.

sh
curl -sS -X POST "$API_BASE/usm/session/create" \
  -H 'Content-Type: application/json' \
  -d '{"email":"'$EMAIL'","passcode":"'$PASSCODE'","caption":"cli","session_label":"cli"}'

Capture:

  • session_guid -> SESSION_GUID
  • user_id -> USER_GUID

2) Create org (OFM)

sh
curl -sS -X POST "$API_BASE/ofm/org/create" \
  -H 'Content-Type: application/json' \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"orgcode":"'$ORGCODE'","invitation_code":"'$INVITE_CODE'","user_guid":"'$USER_GUID'","caption":"Acme HQ"}'

Capture:

  • org_guid
  • revision (org record revision)
  • cost_centre.cc_guid (optional but useful later)

3) Verify org (operator-only)

Intentional Operator Gate

Org verification is an intentional security gate. New orgs are created in unverified status and cannot perform org-scoped writes until an operator verifies them via direct Lambda (IAM). This prevents unauthorized tenants from writing data. If you skip this step, all downstream writes (PVM, PMC, ICS, etc.) return 403 org-write-blocked.

Direct Lambda payload for ofm_orgstatusset:

json
{ "org_guid": "ORG_GUID", "status": "verified", "expected_revision": "ORG_REV", "reason": "onboarding" }

To invoke in a sandbox environment:

sh
aws lambda invoke \
  --function-name ofm_orgstatusset \
  --cli-binary-format raw-in-base64-out \
  --payload '{"org_guid":"ORG_GUID","status":"verified","expected_revision":"ORG_REV","reason":"onboarding"}' \
  --profile g3nretailstack \
  /dev/stdout

For the full ofm_orgstatusset payload shape, see OFM Surfaces.

4) Create facilities (OFM)

You need a logical facility to create a sales channel.

4a) Physical facility

sh
curl -sS -X POST "$API_BASE/ofm/facility/physical/create" \
  -H 'Content-Type: application/json' \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"org_guid":"ORG_GUID","code":"PHYS1","caption":"HQ","address":{"street":"1 Main St","city":"Austin","region":"TX","country":"US"},"phone":"+1-555-0100"}'

Capture physical_guid.

sh
curl -sS -X POST "$API_BASE/ofm/facility/legal/create" \
  -H 'Content-Type: application/json' \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"org_guid":"ORG_GUID","code":"LEG1","caption":"Acme LLC","address":{"street":"1 Main St","city":"Austin","region":"TX","country":"US"}}'

Capture legal_guid.

4c) Logical facility

sh
curl -sS -X POST "$API_BASE/ofm/facility/logical/create" \
  -H 'Content-Type: application/json' \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"org_guid":"ORG_GUID","code":"LOG1","caption":"Online DC","physical_guid":"PHYS_GUID","legal_guid":"LEG_GUID"}'

Capture logical_guid.

5) Create a sales channel (OFM)

sh
curl -sS -X POST "$API_BASE/ofm/sales-channel/create" \
  -H 'Content-Type: application/json' \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"org_guid":"ORG_GUID","logical_guid":"LOGICAL_GUID","channel_code":"WEB","market_code":"US","locale_codes":["en-US"],"default_locale_code":"en-US"}'

Capture:

  • channel_guid
  • revision (sales channel revision)

Activate the channel (owner-only):

sh
curl -sS -X POST "$API_BASE/ofm/sales-channel/status" \
  -H 'Content-Type: application/json' \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"org_guid":"ORG_GUID","channel_guid":"CHANNEL_GUID","status":"active","expected_revision":"CHANNEL_REV","reason":"go-live"}'

6) Create taxonomy + suppliers (PVM)

PVM POSTs should use the x-session-guid header (canonical); body session_guid is accepted for compatibility. orgcode is required (header shown below).

6a) Division / department / category

sh
curl -sS -X POST "$API_BASE/pvm/division" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"code":"DIV1","caption":"Division 1"}'

Capture division_id and revision, then activate:

sh
curl -sS -X POST "$API_BASE/pvm/division/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"division_id":"DIVISION_ID","status":"active","expected_revision":"DIVISION_REV"}'

Department:

sh
curl -sS -X POST "$API_BASE/pvm/department" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"division_id":"DIVISION_ID","code":"DEPT1","caption":"Department 1"}'

Activate:

sh
curl -sS -X POST "$API_BASE/pvm/department/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"department_id":"DEPARTMENT_ID","status":"active","expected_revision":"DEPARTMENT_REV"}'

Category:

sh
curl -sS -X POST "$API_BASE/pvm/category" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"department_id":"DEPARTMENT_ID","code":"CAT1","caption":"Category 1"}'

Activate:

sh
curl -sS -X POST "$API_BASE/pvm/category/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"category_id":"CATEGORY_ID","status":"active","expected_revision":"CATEGORY_REV"}'

6b) Vendor + manufacturer

sh
curl -sS -X POST "$API_BASE/pvm/vendor" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"code":"VEND1","caption":"Vendor 1"}'

Verify vendor:

sh
curl -sS -X POST "$API_BASE/pvm/vendor/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"vendor_id":"VENDOR_ID","status":"verified","expected_revision":"VENDOR_REV"}'

Manufacturer mirrors vendor (/pvm/manufacturer + /pvm/manufacturer/status).

sh
curl -sS -X POST "$API_BASE/pvm/manufacturer" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"code":"MFG1","caption":"Manufacturer 1"}'

curl -sS -X POST "$API_BASE/pvm/manufacturer/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"manufacturer_id":"MANUFACTURER_ID","status":"verified","expected_revision":"MANUFACTURER_REV"}'

7) Create style + variant (PVM)

7a) Style

sh
curl -sS -X POST "$API_BASE/pvm/style" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"code":"STYLE1","category_id":"CATEGORY_ID","vendor_ids":["VENDOR_ID"],"manufacturer_ids":["MANUFACTURER_ID"],"vendor_primary":"VENDOR_ID","manufacturer_primary":"MANUFACTURER_ID"}'

Activate:

sh
curl -sS -X POST "$API_BASE/pvm/style/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"style_id":"STYLE_ID","status":"active","expected_revision":"STYLE_REV"}'

7b) Variant (with empty selections if OGM has no groups)

sh
curl -sS -X POST "$API_BASE/pvm/variant" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"style_id":"STYLE_ID","selections":[],"code":"VAR1"}'

Activate:

sh
curl -sS -X POST "$API_BASE/pvm/variant/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"style_id":"STYLE_ID","variant_id":"VARIANT_ID","status":"active","expected_revision":"VARIANT_REV"}'
sh
curl -sS -X POST "$API_BASE/pvm/identifier/add" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"style_id":"STYLE_ID","variant_id":"VARIANT_ID","type":"sku","value":"SKU-001"}'

8) Publish (PMC)

PMC requires x-orgcode and x-session-guid headers.

8a) Start a publish run

sh
curl -sS -X POST "$API_BASE/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"}'

Capture run_id and revision (RUN_REV).

8b) Step the run until completed

sh
curl -sS -X POST "$API_BASE/pmc/publish/run/step" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"run_id":"RUN_ID","expected_revision":"RUN_REV"}'

If the response status is still running, repeat the step call using the latest revision from the response.

8c) Fetch the published product

sh
curl -sS -G "$API_BASE/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"

Expected revision + 409 conflict handling

Whenever you update an existing record, include expected_revision from your most recent read.

If a write returns 409 conflict:

  1. Re-read the record (GET or the relevant .../get endpoint).
  2. Use the current revision from the response.
  3. Retry the write with expected_revision set to that value.

Example (style status):

sh
# 1) Read current style
curl -sS -G "$API_BASE/pvm/style/get" \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  --data-urlencode "style_id=STYLE_ID"

# 2) Retry with latest revision
curl -sS -X POST "$API_BASE/pvm/style/status" \
  -H 'Content-Type: application/json' \
  -H "x-orgcode: $ORGCODE" \
  -H "x-session-guid: $SESSION_GUID" \
  -d '{"style_id":"STYLE_ID","status":"active","expected_revision":"LATEST_REV"}'

If a write returns 428 expected-revision-required, you attempted to update an existing record without providing expected_revision.