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 |
|---|---|---|---|
|
The upstream |
The upstream’s lifetime of the record |
Opaque string. Should be the upstream’s natural key (SKU code, document number, bin path). |
|
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 |
|---|---|
SKU |
|
UoM |
|
Warehouse |
|
Zone |
|
Bin |
|
Receiver |
|
Shipper |
|
Lot |
|
Serial |
|
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? |
|---|---|
|
No. It is the lookup key. |
|
No. FG.AI never reassigns. |
|
No. Tenant identity is fixed. |
Business attributes (name, description, base_uom, location parent, …) |
Yes. New values overwrite old. |
|
Yes, must increase. |
Lifecycle status (e.g. |
Yes. |
Renaming¶
If the upstream genuinely needs to rename an entity (a SKU code changes, a bin gets re-coded), the recommended path is:
Mark the old entity
INACTIVE.Upsert the new entity with the new
source_id.Optionally, register the mapping as a known alias via the
/aliasesendpoint (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: INACTIVEto retire a master record.Use
DELETE /wms-ingest/v1/documents/{source_id}?type=…to cancel a document.For inventory, use the
ADJUSTmovement kind, not a deletion.
Physical deletion is not supported through the ingest API. Audit retention requires the record to remain queryable.