Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Initial public API release. Stable contract for paths, field names, response sha

Two identifier-model clarifications that sharpen the line between the surrogate `id` and the `external_key` handle. Pre-launch; no `v1.0.0`-or-later wire baseline to break. The `external_key` item is a docs correction only (service behavior unchanged); the `id` item reflects the move to a single shared id sequence landing under the hood.

- **The surrogate `id` is globally unique across every resource type, opaque, and permanent.** No two rows — of any type — share an `id`; an `id` never changes and is never reused, because there is no hard delete and the shared id sequence is never reseeded. The prior docs described ids as unique only within an entity type (so the same integer could be both an asset id and a tag id). That cross-type collision is eliminated: ids are now minted from one shared sequence, so global uniqueness holds by construction. Treat `id` as opaque (don't parse it, order by it, or infer a count or creation time) and use it as your durable foreign key. See [ID format](./id-format) and [Resource identifiers → Numeric `id` is a surrogate key](./resource-identifiers#numeric-id-is-a-surrogate-key).
- **The surrogate `id` is globally unique across every resource type, opaque, and permanent.** No two rows — of any type — share an `id`; an `id` never changes and is never reused, because the API never hard-deletes and the shared id sequence is never reseeded. The prior docs described ids as unique only within an entity type (so the same integer could be both an asset id and a tag id). That cross-type collision is eliminated: ids are now minted from one shared sequence, so global uniqueness holds by construction. Treat `id` as opaque (don't parse it, order by it, or infer a count or creation time) and use it as your durable foreign key. See [ID format](./id-format) and [Resource identifiers → Numeric `id` is a surrogate key](./resource-identifiers#numeric-id-is-a-surrogate-key).
- **`external_key` auto-mint is `MAX(live key) + 1`, not a lowest-unused-slot allocator.** Earlier wording ("lowest unused slot among live rows," "gap-filling") was incorrect: the server takes the highest live `ASSET-NNNN` / `LOC-NNN` in your org and adds one, zero-padded (`ASSET-%04d` / `LOC-%03d`). A middle-of-range delete therefore leaves a *permanent* gap that is never backfilled; only deleting the current highest live key frees its number for re-issue. A separate, caller-initiated path still lets you re-supply a soft-deleted key string directly (the partial unique index is `WHERE deleted_at IS NULL`, so deleted keys sit outside it). The [auto-mint section on Resource identifiers](./resource-identifiers#external_key-is-optional-on-create) and the `LOC-NNN` changelog entry below are corrected to match. Service behavior is unchanged; only the docs were wrong.

### Surrogate ids are int64 end-to-end; the int32 ceiling and id `too_large` are retired {#surrogate-ids-int64}
Expand Down
2 changes: 1 addition & 1 deletion docs/api/id-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The declared `maximum` is the **JS-safe-integer cap** (2⁵³−1), not a storag

## Why int64, and why the 2⁵³−1 cap

The surrogate `id` is a high-entropy, server-assigned integer — **globally unique across every resource type** (no two rows, of any type, share an `id`), **opaque** (don't parse it, order by it, or infer a count or creation time from it), and **permanent** (it never changes and is never reused — there is no hard delete, and the shared id sequence is never reseeded). It is scattered across a wide range rather than monotonically assigned from `1`. Declaring `int64` from v1 launch means a future widening of the id space is a non-event for typed clients — they are already typed wide enough:
The surrogate `id` is a high-entropy, server-assigned integer — **globally unique across every resource type** (no two rows, of any type, share an `id`), **opaque** (don't parse it, order by it, or infer a count or creation time from it), and **permanent** (it never changes and is never reused — the API never hard-deletes, and the shared id sequence is never reseeded). It is scattered across a wide range rather than monotonically assigned from `1`. Declaring `int64` from v1 launch means a future widening of the id space is a non-event for typed clients — they are already typed wide enough:

- Java / Kotlin clients surface ids as `Long`.
- C# clients surface `long`.
Expand Down
2 changes: 1 addition & 1 deletion docs/api/resource-identifiers.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ This is the conventional REST shape and the URL stays valid even if the asset's

### Numeric `id` is a surrogate key

Numeric `id` values are surrogate keys — server-assigned, high-entropy, and **globally unique across every resource type**: no two rows, whether two assets or an asset and a tag, ever share an `id`. They are **opaque** (don't parse an `id`, order by it, or infer a count or creation time from it) and **permanent** (an `id` never changes and is never reused — there is no hard delete, and ids are minted from a single shared sequence that is never reseeded). The API still carries each id with its entity context — by URL position (`/assets/{asset_id}`, `/locations/{location_id}/tags/{tag_id}`) or query-parameter name (`location_id`, `parent_id`) — and client code matches ids to their entity type as standard surrogate-key discipline. Use `id` as your durable foreign key when you mirror TrakRF data.
Numeric `id` values are surrogate keys — server-assigned, high-entropy, and **globally unique across every resource type**: no two rows, whether two assets or an asset and a tag, ever share an `id`. They are **opaque** (don't parse an `id`, order by it, or infer a count or creation time from it) and **permanent** (an `id` never changes and is never reused — the API never hard-deletes, and ids are minted from a single shared sequence that is never reseeded). The API still carries each id with its entity context — by URL position (`/assets/{asset_id}`, `/locations/{location_id}/tags/{tag_id}`) or query-parameter name (`location_id`, `parent_id`) — and client code matches ids to their entity type as standard surrogate-key discipline. Use `id` as your durable foreign key when you mirror TrakRF data.

Every surrogate id is declared **int64** (`format: int64`, `maximum: 9007199254740991`) on the spec — the same type and ceiling on response bodies, request bodies, `_id` query filters, and path parameters. The `maximum` is JavaScript's `Number.MAX_SAFE_INTEGER` (2⁵³−1), so every admissible id is exactly representable in a `number`-typed client. See [ID format: int64 wire, int64 runtime](./id-format) for the rationale, the id-boundary error envelopes, and what it means for typed-client codegen.

Expand Down
Loading