Side-partner integration (Path C)¶
This page documents Path C — Side-partner integration shim, the third integration topology for third-party WMS partners that need to integrate with FG.AI but don’t justify a full wes-contract (Path A) implementation and can’t wait for FlexSync v1 (Path B). The authoritative ADR is WMS ADR-0045.
If you’re a 3rd-party WMS vendor reading this page, you almost certainly came here from a sales conversation that determined Path C fits your engagement. If you’re not sure which path fits, start at the WMS dev intro and the wes-contract intro.
The three paths¶
Path |
Integration shape |
Inbound (you → FG.AI) |
Outbound (FG.AI → you) |
Use when |
|---|---|---|---|---|
A — |
You implement the public WMS↔WES contract |
|
|
API-capable WMS with engineering capacity AND strategic priority justifying the per-customer investment |
B — FlexSync normalization |
FGAI ships a per-vendor FlexSync connector |
FlexSync writes to FG.AI WMS public REST on your behalf |
FlexSync v2 bidirectional (future) |
Legacy / API-poor WMS; named connectors in the FlexSync v1 roadmap |
C — Side-partner shim |
You adapt to FG.AI public REST inbound; FGAI ships |
You translate your payloads to FG.AI shape; POST to |
Per-partner outbound adapter subscribes to FG.AI Kafka and translates to your webhook/REST shape |
Side-partner strategic relationship; cannot wait 6-9 months for FlexSync v1; partner has bearer-token-style REST API |
Path C is time-boxed: at most two side partners may onboard via Path C before FlexSync v1 GA. The third side partner waits for FlexSync v1 and becomes a FlexSync connector candidate. This is enforced at the WMS partner registry config level (MAX_SIDE_PARTNERS=2).
Architecture at a glance¶
You (3rd-party WMS)
│ ▲
│ │
inbound: outbound:
you → FGAI public REST wes-{you}-adapter → you
│ │
▼ │
FG.AI WMS public API
├── pim/v1/ (SKU + location master)
├── wms/v1/ (work orders + documents)
└── wms-partners/v1/ (partner-scoped read-only views)
│
▼
Kafka event stream
│
▼
wes-{you}-adapter
(per-partner outbound translator)
│
└─► your webhook/REST
The architectural ground rule: FG.AI WMS public REST is the side-partner inbound boundary. FG.AI does NOT define partner-shape REST endpoints inside its core. You adapt to FG.AI shape on your side. This is the architectural firewall that prevents per-partner contract sprawl.
Inbound — you adapt to FG.AI public REST¶
For Path C inbound, you translate your payload shape to FG.AI WMS shape and POST to the existing public surface. The four typical interfaces map as follows:
Your interface |
FG.AI destination |
Requirement category |
|---|---|---|
SKU master push (your |
|
MD-01, MD-02 (Phase 1) |
Location master push (your |
|
MD-07 (Phase 1) |
Work order push (your |
|
DOC / WO category (Phase 1) |
Inventory delta query |
|
PART-02 (Phase 8 v1.1) |
The shape mapping is your responsibility. FG.AI does not absorb your contract surface. The benefit of this constraint: the same Path C inbound mapping you write today is the foundation of the future FlexSync {you}-wms connector when you later onboard as a FlexSync connector post-FlexSync-v1 GA (per ADR-0045 §7).
Authentication for pim/v1/ and wms/v1/ writes: during the UAT bridge period (today, against wms-partners-bridge-service deployed to dev-api.flexgalaxy.com), writes use the same RS256 partner-bearer-JWT issued at POST /wms-partners/v1/authenticate — a transitional convenience that lets partners exercise one auth flow during UAT. At bridge retirement (when WMS Phase 1 + Phase 2 ship; coordinated with ≥30 days advance notice), the auth scheme transitions to the canonical FG.AI partner-auth per authentication.md — mTLS at EKS ingress in production (api.flexgalaxy.ai, api.flexgalaxy.com); API key bearer token in dev (dev-api.flexgalaxy.com). The wms-partners/v1/ partner-bearer-JWT remains canonically bounded to reads + /authenticate only (per ADR-0045 §4) post-retirement. See side-partner-interface-mapping § Authentication for inbound writes for the per-stage table.
Idempotency is enforced via the canonical (partner_id, correlation_id) tuple (see idempotency). The server-assigned control_number returned in responses is a stable reference for audit/correlation, not the caller-supplied idempotency key. Path C rows carry a source field tag indicating they originated via Path C.
The one new endpoint — wms-partners/v1/inventory-delta¶
Side partners often need a periodic inventory-state query so they can reconcile their book against FG.AI’s authoritative state. INV-06..11 are tenant-CRUD-shape (DotID JWT auth, tenant-scoped) — not the right shape for partner-authenticated, partner-scoped, change-log queries. So Path C introduces one new endpoint in a new REST family:
GET /wms-partners/v1/inventory-delta?since={timestamp}&limit={N}&cursor={opaque}
Authorization: Bearer {partner-token}
Query parameters (per the published wms-partners/v1 OpenAPI):
Parameter |
Required |
Notes |
|---|---|---|
|
yes |
Inclusive lower bound on |
|
no |
Default 100, max 1000. Larger windows split across multiple cursor pages. |
|
no |
Opaque pagination cursor from a previous response’s |
Response:
{
"since": "2026-05-29T08:00:00Z",
"until": "2026-05-29T08:05:00Z",
"next_cursor": "...",
"deltas": [
{
"sku_code": "SKU-123",
"location_code": "A-12-3-1",
"qty_delta": "+8",
"movement_type": "RECEIVE",
"occurred_at": "2026-05-29T08:02:14Z",
"source_movement_id": "frn:wms:intl:movement/MV-2026-04823"
}
]
}
Returns a paginated change-log of inventory movements scoped to your tenant-of-record. Backed by wms.movement.*.v1 event projections in the reporting-service (Phase 4) with partner-scope filtering applied via the WMS partner registry.
wms-partners/v1/ is reserved for partner-scoped read-only views. No other endpoints are committed in this ADR. Future endpoints under this namespace land via incremental ADR amendments as side-partner needs surface.
Partner-bearer-JWT authentication¶
Side partners exchange credentials for a short-lived RS256 JWT signed by AWS KMS. The token is bounded to the wms-partners/v1/ namespace and is verifiable locally via the partition-scoped JWKS endpoint.
POST /wms-partners/v1/authenticate
Content-Type: application/json
{ "partner_app_id": "...", "partner_app_secret": "..." }
HTTP/1.1 200 OK
{ "access_token": "eyJhbGciOiJSUzI1NiIs...", "token_type": "Bearer", "expires_in": 7200 }
You are provisioned with
(partner_app_id, partner_app_secret)in the WMS partner registry (which extends the Phase 8 EDI partner registry).Exchange credentials for a JWT. TTL is configurable per partner; default is 7200 seconds (120 minutes).
Token format is JWT (RS256) signed via AWS KMS asymmetric key. The header carries a
kidclaim identifying the KMS key version that signed it; verifiers select the matching JWKS entry bykid.Token claims:
iss(partition-scoped issuer URL),aud=wms-partners-v1,sub=frn:wms:partner:{your_code},tenant_id(when binding is active),scope(space-separated, e.g.wms-partners:inventory-delta:read),iat/exp/jti.Rotation and revocation via the partner-registry admin UI. KMS key rotation is transparent: the JWKS endpoint exposes both the current and previous-rotation-window public keys so in-flight tokens validate cleanly during the transition window (default 7 days).
JWKS endpoint — local JWT verification (recommended)¶
GET /wms-partners/v1/.well-known/jwks.json
Cache-Control: public, max-age=300
Returns an RFC 7517 JSON Web Key Set with the active + retiring RSA public keys. Standard JWT libraries (Spring Security NimbusJwtDecoder, Node jose, Python PyJWT, Go golang-jwt) consume this directly:
// Node example
import { jwtVerify, createRemoteJWKSet } from 'jose';
const JWKS = createRemoteJWKSet(new URL('https://api.flexgalaxy.ai/wms-partners/v1/.well-known/jwks.json'));
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://api.flexgalaxy.ai/wms-partners/v1',
audience: 'wms-partners-v1',
});
Cache JWKS for the duration of the Cache-Control header (default 300s); refresh on kid not found (handles KMS key version bumps within the cache window).
Partition-scoping. A token issued by the intl partition cannot be validated against the cn partition’s JWKS and vice versa — the KMS key alias and JWKS URL are partition-specific. Aligns with WMS ADR-0020 cross-partition immutability. Always use the JWKS URL of the partition you obtained the token from.
Per-environment hostnames¶
The hostname you use for every Path C call (inbound writes, authenticate, inventory-delta, JWKS) depends on the environment + partition you are integrating against. Pick one and stick to it end-to-end — mixing partitions is the most common JWKS-verification failure.
Environment |
Partition / region |
Hostname |
|---|---|---|
Development (shared dev / pre-UAT) |
dev (AWS ap-northeast-1 Tokyo) |
|
Production — international |
intl (AWS ap-northeast-1 Tokyo) |
|
Production — China mainland |
cn (AWS cn-northwest-1 Ningxia) |
|
Concrete URL examples by partition:
Surface |
dev |
prod-intl |
prod-cn |
|---|---|---|---|
Authenticate |
|
|
|
JWKS |
|
|
|
Inventory delta |
|
|
|
SKU push |
|
|
|
Location push |
|
|
|
Work order push |
|
|
|
The iss claim in JWTs issued by FG.AI matches the wms-partners/v1 URL of the issuing partition — verifiers MUST validate iss exactly (no wildcards) and reject any token whose iss does not match their expected partition.
You MAY treat the JWT as opaque and let FG.AI validate per request — but local verification is preferred (zero round-trips, no per-request availability dependency on FG.AI for token validation).
This pattern is bounded to wms-partners/v1/ reads + authenticate. Partner-pushed writes via pim/v1/, wms/v1/, wms-docs/v1/ use the canonical FG.AI partner-auth scheme (mTLS in prod, API key in dev — see “Authentication for pim/v1/ and wms/v1/ writes” above), not this JWT pattern. The bounded scope keeps the partner-bearer-JWT shape from polluting the DotID JWT canon (REST inbound) or the mTLS-at-EKS canon (event-pushing partners).
Outbound — wes-{you}-adapter¶
The outbound translator is its own standalone module per partner. Repo convention: SiriusVoyager/wes-{partner-code}-adapter, scaffolded from wes-partner-adapter-template at onboarding time.
Aspect |
Convention |
|---|---|
Subscribes to |
|
Translates to |
Your outbound REST/webhook shape with per-partner outbound auth (bearer-token caching, refresh-on-expiry, retry/backoff). |
Deployment |
Cloud-only (per FlexSync ADR-0024 cloud-default for cloud-accessible partners); per-tenant; small footprint (~200-500 LoC + deployment scaffolding; ≤ 200 MB Spring Boot or Quarkus container). |
Auth model |
Outbound bearer-token to your auth endpoint (cached, refreshed on expiry). Inbound callbacks (if any) go through |
Retirement criterion |
When FlexSync v1 ships AND you onboard as a FlexSync connector (per Path B), the |
The adapter is explicit technical debt with a known sunset condition — not perpetual sustaining engineering. The retirement is a contractual milestone of the Path C agreement.
Deployment topology — one release per (partner, tenant)¶
Each Path C (partner, tenant) pair gets its own Helm release in a shared partner-adapters k8s namespace. For a partner serving N FG.AI tenants, FG.AI runs N pods of wes-{partner}-adapter, each bound to one tenant — per-tenant Kafka consumer-group, per-tenant Postgres schema, per-tenant DotID service account, per-tenant credentials. This matches FG.AI’s overall multi-tenant canon (WES ADR-0020 per-tenant Kafka prefix; FlexSync per-tenant connector isolation per ADR-0024 §5) and makes the eventual FlexSync migration a 1:1 swap.
Operationally, your tenant code lands in the partner_tenant_binding row in FG.AI’s partner registry (see “Partner-tenant binding lifecycle” below) and parameterizes the Helm install.
Partner-tenant binding lifecycle¶
Every Path C (partner, tenant) pair is recorded in the WMS partner registry’s partner_tenant_binding table with a three-state lifecycle:
State |
Meaning |
|---|---|
active |
The (partner, tenant) adapter is running in production; events flow normally. |
migrating |
The tenant is being migrated to a FlexSync connector (post-FlexSync-v1 GA). Adapter still runs but with explicit FlexSync-cutover coordination. |
retired |
Migration complete; Helm release decommissioned; row preserved for audit. |
The MAX_SIDE_PARTNERS=2 budget counts unique partner_id values where any binding is active or migrating. A partner with multiple tenants counts once toward the budget regardless of tenant count.
State transitions emit wms.partner-registry.binding.{state}.v1 Kafka events; a small orchestration handler decommissions the matching Helm release on retired transition.
What FG.AI commits to under Path C¶
When a partner signs the Path C engagement, FG.AI commits to the following deliverables:
A per-partner outbound adapter (
wes-{partner}-adapter) scaffolded from thewes-partner-adapter-templaterepo. The adapter subscribes to FG.AI Kafka events, aggregates into the partner’s expected shape, and posts to the partner’s outbound endpoints with the partner’s bearer-token auth pattern (cached, refreshed on expiry).A per-
(partner, tenant)Helm release. Each FG.AI tenant gets its own pod with isolated Kafka consumer-group, isolated Postgres schema, isolated DotID service account, isolated credentials. Adding tenants doesn’t require code changes.The
wms-partners/v1/inventory-deltaendpoint as a partner-scoped read surface for inventory-state polling. This endpoint is reusable across all Path C partners — it isn’t built per-partner.Partner-bearer-token auth pattern at
POST /wms-partners/v1/authenticatefor partner-scoped reads (see “Partner-bearer-token authentication” above).A
(partner, tenant)binding row in the WMS partner-registry (PART-05) recording the integration’s lifecycle state.Retirement coordination when the partner onboards as a FlexSync connector post-FlexSync-v1 GA, including drain + cutover + decommission of the
wes-{partner}-adapterHelm releases.
In turn, the partner commits to:
Inbound translation from their payload shape to FG.AI WMS public REST. They write the code; FG.AI provides the OpenAPI specs (
pim/v1/,wms/v1/,wms-partners/v1/).Partner-issued credentials (
partner_app_id+partner_app_secret) for both their inbound calls into FG.AI (wms-partners/v1/authenticate) and FG.AI’s outbound calls into them (their/authenticateendpoint).Documented partner IP allowlist per environment, so FG.AI can configure the per-
(partner, tenant)NetworkPolicy egress permissions correctly.Onboarding as a FlexSync connector post-FlexSync-v1 GA. This is the contractual retirement clause of the Path C agreement, not an optional cleanup.
Implementation status¶
The scaffolding for Path C has landed:
Repo |
Purpose |
|---|---|
Scaffolding template — parent POM + |
The template’s chained inheritance: spring-boot-starter-parent:4.0.6 → com.flexgalaxy:fgai-service-template-parent → com.flexgalaxy:wes-partner-adapter-parent → per-partner adapter. Per-partner adapter repos are created from the template via scripts/bootstrap-adapter.sh at onboarding time; they are private under SiriusVoyager/.
If you are a partner engineering team reading this — your work begins at “Inbound” above. The outbound side is FG.AI’s responsibility and runs out of a per-partner adapter repo scaffolded from the template at onboarding time. See side-partner-architecture for the adapter’s runtime shape, side-partner-interface-mapping for the mapping patterns FG.AI uses on the outbound side, side-partner-onboarding for the onboarding sequence, and side-partner-operations for day-2 operational expectations.
Why FG.AI doesn’t define partner-shape endpoints¶
This is the architectural firewall. The alternative — FG.AI defining partner-shape endpoints inside the WMS core for every side partner — would set the precedent that FG.AI absorbs every side partner’s contract surface. Per N side partners, that’s N partner-specific endpoint families bloating the WMS public REST surface and creating per-partner branching deep in the platform. Exactly what WMS ADR-0022 + ADR-0024 + the FlexSync framework were designed to prevent.
Path C’s §2 inbound boundary keeps the surface area discipline:
Partner does inbound translation. They translate their shape to FG.AI shape on their side.
FG.AI does outbound translation, per-partner, in its own ephemeral module. With a known retirement criterion.
Net partner engineering effort is roughly half of a Path A implementation. They write the inbound side; FG.AI writes the outbound side.
This split is what makes Path C viable as a side-partner-scoped exception without devolving into permanent partner-specific scaffolding inside the core platform.
Constraints and pitfalls¶
The 2-partner time-box is hard-enforced. Onboarding a third side partner before FlexSync v1 GA requires either (a) FlexSync v1 in production, OR (b) an explicit ADR-0045 amendment with documented justification. Sales conversations that lean on Path C for a third partner before FlexSync v1 GA must surface the time-box constraint upfront.
No reconciliation framework. Unlike FlexSync v1 (which ships reconciliation as a framework capability per FlexSync PROJECT.md §”Reconciliation”), Path C side partners get periodic-snapshot drift detection only via partner-side polling against
wms-partners/v1/inventory-delta. Acceptable for v1.x side-partner scope; revisit if a Path C partner reports drift incidents.Path C-specific lineage tag. WMS aggregates carry a
sourcefield per ADR-0024 Open Question §2. Path C rows are taggedSIDE_PARTNER:{partner_code}so downstream pipelines (audit, billing, FlexSync v2 migration) can identify Path C lineage distinctly from NATIVE, EDI, and FLEXSYNC sources.You must adapt to FG.AI’s domain model. SKU, location, work-order, document semantics follow FG.AI WMS conventions per WMS ADR-0008. Your
productCodebecomes oursku_code; yourlocationCodebecomes ourlocation_code; yourwoCodebecomes ourwork_order_code. The translation lives in your code, not ours.Partner-bearer-token TTL is short. Default 120 minutes; you must implement refresh-on-expiry on the inbound side. The
wes-{you}-adaptersimilarly refreshes your outbound token on expiry.
What this is not¶
Not a permanent integration path. Path C exists as a bounded exception, not a third permanent topology. The two-partner time-box prevents pattern creep.
Not a FlexSync replacement. FlexSync v1 ships post-WMS-v1.2 with technical-merit-picked first connector (NetSuite). Path C does not pull FlexSync sequencing forward and does not reorder its first-connector slot.
Not a license to define partner-shape endpoints in FG.AI core. The
wms-partners/v1/inventory-deltaendpoint is the only new partner-scoped surface committed in ADR-0045. Future endpoints under that namespace require incremental ADR amendments.