Identity mapping

Every entity that flows through the ingest API has two identities: the upstream’s natural key (source_id) and the identity FG.AI assigns (internal_id). The mapping is stable, queryable, and the basis for idempotent upserts.

The two identifiers

Identifier

Owned by

Stable across

Format

source_id

The upstream

The upstream’s lifetime of the record

Opaque string. Should be the upstream’s natural key (SKU code, document number, bin path).

internal_id

FG.AI WMS

The FG.AI lifetime of the record

Opaque string. FG.AI generates on first acceptance.

The pair (partner_id, source_id) uniquely identifies an entity inside FG.AI. On the first successful upsert, FG.AI assigns an internal_id and returns it; on every subsequent upsert with the same (partner_id, source_id), FG.AI updates the same entity.

Choosing a source_id

The cleanest source_id is the upstream’s primary key. For example:

Entity

Suggested source_id

SKU

SKU-WIDGET-RED-LG (catalog code)

UoM

EA, KG, CTN-12

Warehouse

WH-Tokyo-01

Zone

WH-Tokyo-01.A (warehouse + zone code)

Bin

WH-Tokyo-01.A.12.3.1 (full dot-separated path)

Receiver

RCV-2026-005512

Shipper

SH-2026-000183

Lot

LOT-2026-04-15-XYZ

Serial

SN-001-A-99812

When the upstream’s natural key is composite, concatenate with a stable separator (recommended: . or :) and document the convention. Once a source_id is in use, it cannot change — the mapping is keyed on it, and a rename effectively creates a new entity.

Per-item versioning (source_version)

Each item upsert may carry a monotonically-increasing source_version. If FG.AI already holds a higher version for the same (partner_id, source_id), the incoming upsert is ignored — the response reports status: REPLAY and internal_id is unchanged. This makes out-of-order delivery safe.

{ "source_id": "SKU-WIDGET-RED-LG", "source_version": 12, "name": "Widget Red Large", "base_uom": "EA" }

The cheapest implementation is to increment source_version by one on every source-side update to the row. Time-based versions (epoch milliseconds) also work as long as they are monotone per source_id.

source_version is optional. If you omit it, the upstream is responsible for ordering — out-of-order delivery may overwrite newer state with older. Most production integrations send it.

Looking up the mapping

GET /wms-ingest/v1/mappings?entity=sku&source_id=SKU-WIDGET-RED-LG

Returns:

{
  "entity": "sku",
  "source_id": "SKU-WIDGET-RED-LG",
  "internal_id": "fgai-sku-01HX...",
  "partner_id": "ACME-TENANT-A",
  "first_seen_at": "2026-01-14T03:14:01Z",
  "last_seen_at":  "2026-05-22T11:02:33Z"
}

Most integrations operate purely on source_id and never need to look up the FG.AI internal_id. The endpoint is useful for support: “what is FG.AI’s identifier for our SKU X?” Or for cross-system reconciliation when the upstream’s audit trail needs to record the FG.AI side too.

Multi-tenant upstreams

If the upstream is multi-tenant, one partner_id per source-side tenant. Format:

{source-system-code}-TENANT-{tenant-code}

Examples:

ACME-TENANT-A
ACME-TENANT-B
LEGACY-WMS-TENANT-001

The same source_id under two different partner_ids is two different FG.AI entities. The partner registry enforces per-tenant scoping at the PDP — a credential issued for ACME-TENANT-A cannot push into ACME-TENANT-B.

What can change after first ingest

Field

Mutable on subsequent upserts?

source_id

No. It is the lookup key.

internal_id

No. FG.AI never reassigns.

partner_id

No. Tenant identity is fixed.

Business attributes (name, description, base_uom, location parent, …)

Yes. New values overwrite old.

source_version

Yes, must increase.

Lifecycle status (e.g. ACTIVEINACTIVE)

Yes.

Renaming

If the upstream genuinely needs to rename an entity (a SKU code changes, a bin gets re-coded), the recommended path is:

  1. Mark the old entity INACTIVE.

  2. Upsert the new entity with the new source_id.

  3. Optionally, register the mapping as a known alias via the /aliases endpoint (deferred — out of scope for v1).

Avoid silently re-using a source_id for a different real-world entity. The audit trail will be unrecoverable.

Tombstoning

Delete by tombstone, not by physical removal:

  • Upsert with lifecycle: INACTIVE to retire a master record.

  • Use DELETE /wms-ingest/v1/documents/{source_id}?type=… to cancel a document.

  • For inventory, use the ADJUST movement kind, not a deletion.

Physical deletion is not supported through the ingest API. Audit retention requires the record to remain queryable.