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

Awes-contract direct

You implement the public WMS↔WES contract

wes-contract REST + proto (12-endpoint surface)

wes-contract events

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 wes-{you}-adapter outbound

You translate your payloads to FG.AI shape; POST to pim/v1/, wms/v1/, wms-partners/v1/

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 productCode + attributes)

POST api.flexgalaxy.com/pim/v1/skus

MD-01, MD-02 (Phase 1)

Location master push (your locationCode + zone/bin attributes)

POST api.flexgalaxy.com/pim/v1/locations

MD-07 (Phase 1)

Work order push (your woCode + taskList[])

POST api.flexgalaxy.com/wms/v1/work-orders

DOC / WO category (Phase 1)

Inventory delta query

GET api.flexgalaxy.com/wms-partners/v1/inventory-delta (see below)

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.mdmTLS 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

since

yes

Inclusive lower bound on occurred_at, RFC 3339. Must be within the retention window (default 7 days; per-partner configurable). Older windows return 400 with the retention bound in the problem-detail body.

limit

no

Default 100, max 1000. Larger windows split across multiple cursor pages.

cursor

no

Opaque pagination cursor from a previous response’s next_cursor. Omit on the first page; pass the previous response’s next_cursor value verbatim to fetch the next page.

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 kid claim identifying the KMS key version that signed it; verifiers select the matching JWKS entry by kid.

  • 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).

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)

dev-api.flexgalaxy.com

Production — international

intl (AWS ap-northeast-1 Tokyo)

api.flexgalaxy.ai

Production — China mainland

cn (AWS cn-northwest-1 Ningxia)

api.flexgalaxy.com

Concrete URL examples by partition:

Surface

dev

prod-intl

prod-cn

Authenticate

POST https://dev-api.flexgalaxy.com/wms-partners/v1/authenticate

POST https://api.flexgalaxy.ai/wms-partners/v1/authenticate

POST https://api.flexgalaxy.com/wms-partners/v1/authenticate

JWKS

GET https://dev-api.flexgalaxy.com/wms-partners/v1/.well-known/jwks.json

GET https://api.flexgalaxy.ai/wms-partners/v1/.well-known/jwks.json

GET https://api.flexgalaxy.com/wms-partners/v1/.well-known/jwks.json

Inventory delta

GET https://dev-api.flexgalaxy.com/wms-partners/v1/inventory-delta

GET https://api.flexgalaxy.ai/wms-partners/v1/inventory-delta

GET https://api.flexgalaxy.com/wms-partners/v1/inventory-delta

SKU push

POST https://dev-api.flexgalaxy.com/pim/v1/skus

POST https://api.flexgalaxy.ai/pim/v1/skus

POST https://api.flexgalaxy.com/pim/v1/skus

Location push

POST https://dev-api.flexgalaxy.com/pim/v1/locations

POST https://api.flexgalaxy.ai/pim/v1/locations

POST https://api.flexgalaxy.com/pim/v1/locations

Work order push

POST https://dev-api.flexgalaxy.com/wms/v1/work-orders

POST https://api.flexgalaxy.ai/wms/v1/work-orders

POST https://api.flexgalaxy.com/wms/v1/work-orders

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

wms.{chain}.*.v1 (document state transitions), wms.movement.*.v1, wes.confirmation.*.v1 (post-Phase 9). Consumer group per partner; idempotent processing.

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 wms-partners/v1/ per the partner-bearer-token pattern above.

Retirement criterion

When FlexSync v1 ships AND you onboard as a FlexSync connector (per Path B), the wes-{you}-adapter is decommissioned. Outbound feedback consolidates into the FlexSync connector’s eventual bidirectional capability (FlexSync v2).

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 the wes-partner-adapter-template repo. 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-delta endpoint 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/authenticate for 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}-adapter Helm 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 /authenticate endpoint).

  • 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

wes-partner-adapter-template

Scaffolding template — parent POM + wes-partner-adapter-common library + bootstrap-adapter.sh + templated child files (pom.xml, application.yml, k8s manifests, GitHub Actions). Sibling to fgai-service-template.

The template’s chained inheritance: spring-boot-starter-parent:4.0.6com.flexgalaxy:fgai-service-template-parentcom.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 source field per ADR-0024 Open Question §2. Path C rows are tagged SIDE_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 productCode becomes our sku_code; your locationCode becomes our location_code; your woCode becomes our work_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}-adapter similarly 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-delta endpoint is the only new partner-scoped surface committed in ADR-0045. Future endpoints under that namespace require incremental ADR amendments.