Appearance
Organization & Facility Management (OFM)
OFM — Organization & Facility Management
Org spine: invitations, orgs, cost centres, facilities (physical/legal/logical), zones, owners, members, teams, and resolvers.
Surface map (contract-only)
- Public API (API Gateway): base
https://api.g3nretailstack.com/ofm; endpoints under/ofm/*. - Direct Lambda (operator-only): invitation ops (
invitationCreate,invitationDoom, etc.) are IAM-gated and are not exposed via API Gateway. All other operations live on API Gateway. - Operator-only verification/freeze: org lifecycle transitions like
unverified → verifiedor any transition tofrozenare IAM-gated (direct Lambda). Until verified, org writes across services return 403org-write-blocked. - Auth: canonical headers
x-session-guid(human USM session) orx-api-key(USM service-account). Bodysession_guid/api_keyaccepted for compatibility. Org-level/governance mutations require ownership (owner session or API-key roleowner); selected facility-scoped mutations can be delegated via explicit logical assignments + facility grants. - Optimistic concurrency: for revisioned records, state-changing operations require
expected_revision; missing returns428 expected-revision-required, mismatch returns409 conflictwith the current record snapshot + currentrevision. - Pagination: default limit 8, clamp 1–256;
next_tokenJSON cursors. - CLI:
cli/bin/g3n.js(g3n ofm ...),--profilerequired;--base-urloverride and JSON--next-tokensupported. - MCP: protocol doc at https://mcp.g3nretailstack.com/ofm/PROTOCOL.md (mirrored here at /ofm/PROTOCOL.md).
- OpenAPI:
https://doc.g3nretailstack.com/ofm/openapi.yaml - Anti-enumeration: non-associated callers receive
404 not-foundon org-scoped reads/lists (see /common/troubleshooting.html). - Governance hardening: association required for org-scoped reads/lists; owner-only operations return
403 not-ownerwhen associated but not owner; service-account reads require a view role (ofm_view/pvv/pma/vca/pmc_view/pmc_publish), else403 forbidden-role. - Role matrix: /common/role-matrix.html
- Headers and identity cheat sheet: /common/headers-identity.html
Responses use { success, data?, error?, build, stats? } with build ID + UTC timestamp in the footer. Stats include call, service, request id, latency, and passthrough context when provided.
Why POST for reads?
OFM uses POST for reads/lists/resolves to keep auth out of URLs, accept structured JSON bodies (org GUIDs, codes, filters), and maintain a uniform surface. Treat read endpoints as POST-only (no GET).
Lifecycles (FSM) and operations
- Terminal doom: for any record with status/state
doomed, OFM rejects further mutations (no transitions out; updates and association changes are blocked). - Invitations: pending → accepted/rejected/expired/doomed. Ops: create (Lambda), reject, get, list, doom (Lambda). Use for controlled org creation/onboarding.
- Organizations: unverified → verified/parked/suspended → frozen → doomed. Ops: create, get, list, update, status set.
frozenis operator-only (UTL offboarding) and blocks org reads/writes across services. - Cost centres: active ↔ suspended → doomed. Ops: create/get/list/update/status set.
cccodeis auto-generated and unique; a master cost centre is auto-created on org create. - Facilities: physical/legal/logical each active ↔ inactive → doomed. Ops: create/get/list/update/status set. Codes unique per org; logical requires physical+legal (same org) and optional
cost_centre_guid. - Zones: active ↔ inactive → doomed; depth ≤32;
ROOTis seeded onfacility/logical/create(andzone/createensures it exists for legacy logicals). Ops: create/get/list/status set. Codes unique per logical; codeROOTis reserved. - Owners: states active/suspended/doomed. Ops: list, primary set, secondary add/remove, state set. Owners outrank members.
- Members: states active/suspended/doomed. Ops: invite-create, invite-accept, state set, assign-logical, detach-logical, list, assignments.
- Teams: active/suspended/doomed. Ops: create/get/list/update/status set, member add/remove, members, by-member (org-scoped; self allowed, other users require owner).
- Resolvers: orgcode→org_guid, invitation code→inv_guid, facility/zone codes scoped to org/logical, cccode→cc_guid.
Governance hardening (auth + anti-enumeration)
- Non-associated callers receive
404 not-foundon org-scoped reads/lists/resolves (anti-enumeration). - Suspended members are treated as non-associated (
404 not-found). - Suspended owners are still associated, but owner-only operations return
403 not-owner. - Owner-only endpoints return
403 not-ownerfor associated non-owners. - Service accounts must be org-bound; mismatched org returns
404 not-found. Reads require a view role (ofm_view,pvv,pma,vca,pmc_view, orpmc_publish) orowner; otherwise403 forbidden-role. - Facility-scoped mutations require explicit logical assignments; missing assignment returns
403 forbidden-facility. - Org frozen blocks access across OFM with
403 org-access-blocked. - Tenant writes require
org.status=verified; otherwise409 org-write-blocked.
Clarifications (B3)
- Org lifecycle & write gating: org status must be
verifiedfor tenant writes; operator-only transitions includeunverified → verifiedand any transition tofrozen/doomed. - Owner vs primary owner: primary owner is a single designated owner record (operator-only set) used for governance/audit. Owner-management actions (
owner/primary/set,owner/secondary/add|remove,owner/state/set) require the active primary owner; other owner-only ops (org/cost-centre/facility mutations) allow any active owner. - Master cost centre:
org/createauto-creates a master cost centre and returns it incost_centre;org/getincludescost_centre_guidplus acost_centresnapshot (cc_guid,cccode). - Membership: invite → accept is bound to the invitee; roles can be time-bounded via
effective_from/effective_to; suspending a member blocks access without deleting history. - Facilities: logical facilities represent operational locations; multiple logical facilities per org are supported; physical/legal facilities are the address/legal registries backing logicals.
- Zones: hierarchical with reserved
ROOTcode, depth ≤ 32. Bins are owned by ICS (not OFM). - Sales channels: declarations are org-scoped with
channel_code,market_code, andlocale_codes;channel_guidis the identity. After draft activation,logical_guid/channel_code/market_codeare immutable.
Quick start (happy path)
- Invitation (Lambda):
g3n ofm invitation-create --caption "Q1 invite" --profile g3nretailstack→code. - Org create (API):
POST /org/createwith{ orgcode, invitation_code, user_guid, session_guid }→org_guid+ owners + auto-created master cost centre. - Facilities: create physical + legal, then logical (optional
cost_centre_guid). - Zones: create with
parent_zone_guid(ROOTallowed;ROOTis seeded on logical create); manage lifecycle via status (no update endpoint). - People:
member/invite/create→member/invite/accept, optionalmember/assign-logical,team/createthenteam/member/add;team/by-memberis org-scoped. - Resolve: code→GUID via
resolve/orgcode|invitation|facility|zone|cost-centre.
Examples (full envelope + CLI)
orgCreate (success)
json
{
"success": true,
"data": {
"org_guid": "org-123",
"orgcode": "ACMECORP",
"status": "unverified",
"owners": { "create_owner_user_guid": "user-1", "primary_owner_user_guid": "user-1" },
"cost_centre": { "cc_guid": "cc-1", "cccode": "ABCD-EF12-GHI3" }
},
"build": { "build_major": "MONDAY", "build_minor": "1770263336", "build_id": "MONDAY-1770263336" },
"revision": "rev-org-1",
"stats": { "call": "orgCreate", "service": "ofm", "timestamp_utc": "2026-02-05T00:00:00Z", "request_id": "req-123" }
}CLI:
g3n ofm org-create --orgcode ACMECORP --invitation-code ABC-DEF-1234 --user-guid user-1 --session-guid $SESSION_GUID --profile g3nretailstackorgStatusSet (error: missing expected_revision)
json
{
"success": false,
"error": {
"major": { "tag": "expected-revision-required", "message": { "en_US": "expected_revision required" } },
"details": { "current_revision": "rev-org-1", "current_record": { "org_guid": "org-123", "status": "unverified" } }
},
"build": { "build_major": "MONDAY", "build_minor": "1770263336", "build_id": "MONDAY-1770263336" },
"stats": { "call": "orgStatusSet", "service": "ofm", "timestamp_utc": "2026-02-05T00:00:01Z", "request_id": "req-124" }
}CLI (with expected_revision):
g3n ofm org-status-set --org-guid org-123 --status verified --expected-revision rev-org-1 --session-guid $SESSION_GUID --profile g3nretailstackmemberAssignLogical (success)
json
{
"success": true,
"data": { "org_guid": "org-123", "user_guid": "user-2", "logical_guid": "lq-1", "state": "active" },
"build": { "build_major": "MONDAY", "build_minor": "1770263336", "build_id": "MONDAY-1770263336" },
"stats": { "call": "memberAssignLogical", "service": "ofm", "timestamp_utc": "2026-02-05T00:00:02Z", "request_id": "req-125" }
}CLI:
g3n ofm member-assign-logical --org-guid org-123 --user-guid user-2 --logical-guid lq-1 --role-profile-id inventory_clerk --grants '["facility:zones_write"]' --session-guid $SESSION_GUID --profile g3nretailstackError tags (representative)
invalid-code, invalid-fsm-transition, not-owner, not-found, uniqueness-conflict, code-generation-exhausted, duplicate-member, invitation-consumed, invitation-expired, invalid-depth, invalid-session, forbidden-role, forbidden-facility, org-access-blocked, org-write-blocked, throttled, internal-error.
See Surfaces for payloads, examples, and error conditions per endpoint. Protocol details live at https://mcp.g3nretailstack.com/ofm/PROTOCOL.md (mirrored to /ofm/PROTOCOL.md here).