components:
    headers:
        Allow:
            description: Comma-separated list of HTTP methods supported on this resource (RFC 7231 §6.5.5). Emitted on every 405 response.
            schema:
                type: string
        RateLimitPolicy:
            description: Sustained rate-limit policy as `<quota>;w=<window-seconds>` (e.g. `60;w=60` = 60 requests per 60 seconds). The burst ceiling in X-RateLimit-Limit is higher; throughput sustained above this policy is eventually throttled with a 429.
            schema:
                type: string
        RetryAfter:
            description: Seconds to wait before retrying.
            schema:
                type: integer
        WWWAuthenticate:
            description: RFC 7235 authentication challenge. Always `Bearer realm="trakrf-api"` on 401 responses.
            schema:
                type: string
        XRateLimitLimit:
            description: Maximum requests allowed in a single burst before a 429 — the rate-limit bucket's ceiling. The lower *sustained* rate is advertised separately in the RateLimit-Policy header.
            schema:
                type: integer
        XRateLimitRemaining:
            description: Requests remaining before a 429. Decrements by one on every request, from X-RateLimit-Limit down to 0; clients may pace on this value. Refills over time toward the ceiling at the sustained rate (see RateLimit-Policy).
            schema:
                type: integer
        XRateLimitReset:
            description: Unix timestamp (seconds) when X-RateLimit-Remaining will next equal X-RateLimit-Limit — i.e. when the bucket has fully refilled to the ceiling. Equals the current time when Remaining is already at the ceiling.
            schema:
                type: integer
        XRequestId:
            description: Server-assigned request correlation identifier; mirrored as error.request_id in error envelopes and echoed in server logs. Quote this when filing support tickets.
            schema:
                type: string
    schemas:
        AddAssetTagResponse:
            properties:
                data:
                    $ref: '#/components/schemas/Tag'
            required:
                - data
            type: object
        AddLocationTagResponse:
            properties:
                data:
                    $ref: '#/components/schemas/Tag'
            required:
                - data
            type: object
        AssetHistoryItem:
            properties:
                duration_seconds:
                    format: int32
                    nullable: true
                    type: integer
                event_observed_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    type: string
                location_external_key:
                    nullable: true
                    type: string
                location_id:
                    format: int64
                    maximum: 9007199254740991
                    nullable: true
                    type: integer
            required:
                - event_observed_at
                - location_id
                - location_external_key
                - duration_seconds
            type: object
        AssetHistoryResponse:
            properties:
                data:
                    items:
                        $ref: '#/components/schemas/AssetHistoryItem'
                    type: array
                limit:
                    example: 50
                    format: int32
                    type: integer
                offset:
                    example: 0
                    format: int32
                    type: integer
                total_count:
                    example: 100
                    format: int32
                    type: integer
            required:
                - data
                - limit
                - offset
                - total_count
            type: object
        AssetLocationItem:
            properties:
                asset_deleted_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    nullable: true
                    readOnly: true
                    type: string
                asset_external_key:
                    type: string
                asset_id:
                    format: int64
                    maximum: 9007199254740991
                    type: integer
                asset_last_seen:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    type: string
                location_external_key:
                    nullable: true
                    type: string
                location_id:
                    format: int64
                    maximum: 9007199254740991
                    nullable: true
                    type: integer
            required:
                - asset_id
                - asset_external_key
                - location_id
                - location_external_key
                - asset_last_seen
                - asset_deleted_at
            type: object
        AssetLocationsResponse:
            properties:
                data:
                    items:
                        $ref: '#/components/schemas/AssetLocationItem'
                    type: array
                limit:
                    example: 50
                    format: int32
                    type: integer
                offset:
                    example: 0
                    format: int32
                    type: integer
                total_count:
                    example: 100
                    format: int32
                    type: integer
            required:
                - data
                - limit
                - offset
                - total_count
            type: object
        AssetView:
            properties:
                created_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    readOnly: true
                    type: string
                deleted_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    nullable: true
                    readOnly: true
                    type: string
                description:
                    nullable: true
                    type: string
                external_key:
                    type: string
                id:
                    format: int64
                    maximum: 9007199254740991
                    readOnly: true
                    type: integer
                is_active:
                    type: boolean
                metadata:
                    additionalProperties: true
                    type: object
                name:
                    type: string
                tags:
                    description: Tags currently attached to this resource. Read-only on PATCH; mutate via POST /{resource}/{id}/tags and DELETE /{resource}/{id}/tags/{tag_id}.
                    items:
                        $ref: '#/components/schemas/Tag'
                    readOnly: true
                    type: array
                updated_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    readOnly: true
                    type: string
                valid_from:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    type: string
                valid_to:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    nullable: true
                    type: string
            required:
                - id
                - external_key
                - name
                - description
                - metadata
                - is_active
                - valid_from
                - valid_to
                - created_at
                - updated_at
                - deleted_at
                - tags
            type: object
        BarcodeTag:
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. On this read schema the `tag_type` enum carries `x-extensible-enum: true` (an OpenAPI 3.0 vendor extension) to mark it open to additive variants; promoting that marker to native OpenAPI 3.1 enum semantics is a deferred migration.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            properties:
                id:
                    format: int64
                    maximum: 9007199254740991
                    readOnly: true
                    type: integer
                tag_type:
                    description: Discriminator for the polymorphic Tag resource. `rfid` denotes an RFID transponder, `ble` denotes a Bluetooth Low Energy beacon, and `barcode` denotes a 1D/2D barcode. Together with `value`, this forms the natural key — the same `value` may exist under different `tag_type`s without conflict.
                    enum:
                        - barcode
                    type: string
                    x-extensible-enum: true
                value:
                    maxLength: 255
                    minLength: 1
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
            required:
                - id
                - tag_type
                - value
            type: object
        BarcodeTagRequest:
            additionalProperties: false
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. The request `tag_type` enum is intentionally closed: it carries no `x-extensible-enum` marker, and a write naming an unrecognized variant is rejected as described above.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            properties:
                tag_type:
                    description: Discriminator for the polymorphic Tag resource. `rfid` denotes an RFID transponder, `ble` denotes a Bluetooth Low Energy beacon, and `barcode` denotes a 1D/2D barcode. Together with `value`, this forms the natural key — the same `value` may exist under different `tag_type`s without conflict.
                    enum:
                        - barcode
                    type: string
                value:
                    maxLength: 255
                    minLength: 1
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
            required:
                - tag_type
                - value
            type: object
        BleTag:
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. On this read schema the `tag_type` enum carries `x-extensible-enum: true` (an OpenAPI 3.0 vendor extension) to mark it open to additive variants; promoting that marker to native OpenAPI 3.1 enum semantics is a deferred migration.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            properties:
                id:
                    format: int64
                    maximum: 9007199254740991
                    readOnly: true
                    type: integer
                tag_type:
                    description: Discriminator for the polymorphic Tag resource. `rfid` denotes an RFID transponder, `ble` denotes a Bluetooth Low Energy beacon, and `barcode` denotes a 1D/2D barcode. Together with `value`, this forms the natural key — the same `value` may exist under different `tag_type`s without conflict.
                    enum:
                        - ble
                    type: string
                    x-extensible-enum: true
                value:
                    maxLength: 255
                    minLength: 1
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
            required:
                - id
                - tag_type
                - value
            type: object
        BleTagRequest:
            additionalProperties: false
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. The request `tag_type` enum is intentionally closed: it carries no `x-extensible-enum` marker, and a write naming an unrecognized variant is rejected as described above.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            properties:
                tag_type:
                    description: Discriminator for the polymorphic Tag resource. `rfid` denotes an RFID transponder, `ble` denotes a Bluetooth Low Energy beacon, and `barcode` denotes a 1D/2D barcode. Together with `value`, this forms the natural key — the same `value` may exist under different `tag_type`s without conflict.
                    enum:
                        - ble
                    type: string
                value:
                    maxLength: 255
                    minLength: 1
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
            required:
                - tag_type
                - value
            type: object
        CreateAssetResponse:
            properties:
                data:
                    $ref: '#/components/schemas/AssetView'
            required:
                - data
            type: object
        CreateAssetWithTagsRequest:
            additionalProperties: false
            properties:
                description:
                    example: Main warehouse forklift
                    maxLength: 1024
                    minLength: 1
                    nullable: true
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
                external_key:
                    description: |-
                        external_key is optional. Omit to receive a server-assigned key in the
                        format ASSET-NNNN (per-organization sequence). When supplied, must
                        satisfy the external_key_pattern.
                    example: forklift-3
                    maxLength: 255
                    minLength: 1
                    pattern: ^[A-Za-z0-9-]+$
                    type: string
                is_active:
                    example: true
                    type: boolean
                metadata:
                    additionalProperties: {}
                    type: object
                name:
                    example: Forklift 3
                    maxLength: 255
                    minLength: 1
                    pattern: ^\S(?:[^\x00-\x1F\x7F]*\S)?$
                    type: string
                tags:
                    items:
                        $ref: '#/components/schemas/TagRequest'
                    nullable: true
                    type: array
                valid_from:
                    example: "2025-01-01T00:00:00Z"
                    format: date-time
                    type: string
                valid_to:
                    example: "2026-01-01T00:00:00Z"
                    format: date-time
                    nullable: true
                    type: string
            required:
                - name
            type: object
        CreateLocationResponse:
            properties:
                data:
                    $ref: '#/components/schemas/LocationView'
            required:
                - data
            type: object
        CreateLocationWithTagsRequest:
            additionalProperties: false
            properties:
                description:
                    example: Main warehouse location
                    maxLength: 1024
                    minLength: 1
                    nullable: true
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
                external_key:
                    description: |-
                        external_key is optional. Omit to receive a server-assigned key in the
                        format LOC-NNN (per-organization sequence, 3-digit zero-pad), parallel
                        to (but narrower than) assets' ASSET-NNNN behavior. When supplied,
                        must satisfy the external_key_pattern.
                    example: wh1
                    maxLength: 255
                    minLength: 1
                    pattern: ^[A-Za-z0-9-]+$
                    type: string
                is_active:
                    example: true
                    type: boolean
                name:
                    example: Warehouse 1
                    maxLength: 255
                    minLength: 1
                    pattern: ^\S(?:[^\x00-\x1F\x7F]*\S)?$
                    type: string
                parent_external_key:
                    example: wh1
                    maxLength: 255
                    minLength: 1
                    nullable: true
                    pattern: ^[A-Za-z0-9-]+$
                    type: string
                parent_id:
                    example: 42
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    nullable: true
                    type: integer
                tags:
                    items:
                        $ref: '#/components/schemas/TagRequest'
                    nullable: true
                    type: array
                valid_from:
                    example: "2025-12-14T00:00:00Z"
                    format: date-time
                    type: string
                valid_to:
                    example: "2026-12-14T00:00:00Z"
                    format: date-time
                    nullable: true
                    type: string
            required:
                - name
            type: object
        ErrorEnvelope:
            description: TrakRF error envelope, modeled on RFC 7807 but not 7807-compliant. Carried nested under `error.*` on every error response; content-type is `application/json` (not `application/problem+json`). Generated clients should branch on `type` and `title`, not `detail`. `title` is a stable, machine-readable summary that does not vary between calls for the same condition. `detail` is the specific, human-readable cause of this particular failure and may be empty when title alone fully describes the condition.
            properties:
                detail:
                    description: Specific, human-readable cause of this particular failure. May be empty when title alone fully describes the condition. Do not branch on this value.
                    type: string
                fields:
                    items:
                        $ref: '#/components/schemas/FieldError'
                    type: array
                instance:
                    type: string
                request_id:
                    type: string
                status:
                    format: int32
                    type: integer
                title:
                    description: Stable, machine-readable summary suitable for client-side branching. Does not vary between calls for the same condition.
                    type: string
                type:
                    $ref: '#/components/schemas/ErrorType'
            required:
                - type
                - title
                - status
                - detail
                - instance
                - request_id
            type: object
        ErrorResponse:
            properties:
                error:
                    $ref: '#/components/schemas/ErrorEnvelope'
            required:
                - error
            type: object
        ErrorType:
            description: Machine-readable error envelope discriminator. Pairs with `title` and `detail` to drive client-side branching on a stable token (per RFC 9457).
            enum:
                - validation_error
                - bad_request
                - unauthorized
                - forbidden
                - not_found
                - conflict
                - rate_limited
                - internal_error
                - method_not_allowed
                - unsupported_media_type
                - missing_org_context
            example: validation_error
            type: string
            x-extensible-enum: true
        FieldError:
            properties:
                code:
                    $ref: '#/components/schemas/FieldErrorCode'
                field:
                    type: string
                message:
                    type: string
                params:
                    additionalProperties: {}
                    type: object
            required:
                - field
                - code
                - message
            type: object
        FieldErrorCode:
            description: Machine-readable field-level validation error code. Identifies which constraint a request payload violated; pairs with the field path and human-readable message in a FieldError entry.
            enum:
                - required
                - invalid_value
                - unknown_field
                - too_short
                - too_long
                - too_small
                - too_large
                - fk_not_found
                - ambiguous_fields
                - read_only
                - invalid_context
            example: required
            type: string
            x-extensible-enum: true
        GetAssetResponse:
            properties:
                data:
                    $ref: '#/components/schemas/AssetView'
            required:
                - data
            type: object
        GetCurrentOrgResponse:
            properties:
                data:
                    $ref: '#/components/schemas/OrgView'
            required:
                - data
            type: object
        GetLocationResponse:
            properties:
                data:
                    $ref: '#/components/schemas/LocationView'
            required:
                - data
            type: object
        ListAssetsResponse:
            properties:
                data:
                    items:
                        $ref: '#/components/schemas/AssetView'
                    type: array
                limit:
                    example: 50
                    format: int32
                    type: integer
                offset:
                    example: 0
                    format: int32
                    type: integer
                total_count:
                    example: 100
                    format: int32
                    type: integer
            required:
                - data
                - limit
                - offset
                - total_count
            type: object
        ListLocationAncestorsResponse:
            properties:
                data:
                    items:
                        $ref: '#/components/schemas/LocationView'
                    type: array
                limit:
                    example: 50
                    format: int32
                    type: integer
                offset:
                    example: 0
                    format: int32
                    type: integer
                total_count:
                    example: 100
                    format: int32
                    type: integer
            required:
                - data
                - limit
                - offset
                - total_count
            type: object
        ListLocationChildrenResponse:
            properties:
                data:
                    items:
                        $ref: '#/components/schemas/LocationView'
                    type: array
                limit:
                    example: 50
                    format: int32
                    type: integer
                offset:
                    example: 0
                    format: int32
                    type: integer
                total_count:
                    example: 100
                    format: int32
                    type: integer
            required:
                - data
                - limit
                - offset
                - total_count
            type: object
        ListLocationDescendantsResponse:
            properties:
                data:
                    items:
                        $ref: '#/components/schemas/LocationView'
                    type: array
                limit:
                    example: 50
                    format: int32
                    type: integer
                offset:
                    example: 0
                    format: int32
                    type: integer
                total_count:
                    example: 100
                    format: int32
                    type: integer
            required:
                - data
                - limit
                - offset
                - total_count
            type: object
        ListLocationsResponse:
            properties:
                data:
                    items:
                        $ref: '#/components/schemas/LocationView'
                    type: array
                limit:
                    example: 50
                    format: int32
                    type: integer
                offset:
                    example: 0
                    format: int32
                    type: integer
                total_count:
                    example: 100
                    format: int32
                    type: integer
            required:
                - data
                - limit
                - offset
                - total_count
            type: object
        LocationView:
            properties:
                created_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    readOnly: true
                    type: string
                deleted_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    nullable: true
                    readOnly: true
                    type: string
                description:
                    nullable: true
                    type: string
                external_key:
                    type: string
                id:
                    format: int64
                    maximum: 9007199254740991
                    readOnly: true
                    type: integer
                is_active:
                    type: boolean
                name:
                    type: string
                parent_external_key:
                    nullable: true
                    type: string
                parent_id:
                    format: int64
                    maximum: 9007199254740991
                    nullable: true
                    type: integer
                tags:
                    description: Tags currently attached to this resource. Read-only on PATCH; mutate via POST /{resource}/{id}/tags and DELETE /{resource}/{id}/tags/{tag_id}.
                    items:
                        $ref: '#/components/schemas/Tag'
                    readOnly: true
                    type: array
                updated_at:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    readOnly: true
                    type: string
                valid_from:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    type: string
                valid_to:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    nullable: true
                    type: string
            required:
                - id
                - external_key
                - name
                - description
                - parent_id
                - parent_external_key
                - is_active
                - valid_from
                - valid_to
                - created_at
                - updated_at
                - deleted_at
                - tags
            type: object
        OrgView:
            properties:
                api_key_id:
                    example: 550e8400-e29b-41d4-a716-446655440000
                    type: string
                id:
                    example: 42
                    format: int64
                    maximum: 9007199254740991
                    readOnly: true
                    type: integer
                name:
                    example: Acme Logistics
                    type: string
                scopes:
                    example:
                        - assets:read
                        - assets:write
                    items:
                        type: string
                    type: array
            required:
                - id
                - name
                - scopes
                - api_key_id
            type: object
        RenameAssetRequest:
            additionalProperties: false
            properties:
                external_key:
                    example: ASSET-0042
                    maxLength: 255
                    minLength: 1
                    pattern: ^[A-Za-z0-9-]+$
                    type: string
            required:
                - external_key
            type: object
        RenameAssetResponse:
            properties:
                data:
                    $ref: '#/components/schemas/AssetView'
                descendant_count_affected:
                    example: 0
                    format: int32
                    type: integer
            required:
                - data
                - descendant_count_affected
            type: object
        RenameLocationRequest:
            additionalProperties: false
            properties:
                external_key:
                    example: wh1
                    maxLength: 255
                    minLength: 1
                    pattern: ^[A-Za-z0-9-]+$
                    type: string
            required:
                - external_key
            type: object
        RenameLocationResponse:
            properties:
                data:
                    $ref: '#/components/schemas/LocationView'
                descendant_count_affected:
                    example: 7
                    format: int32
                    type: integer
            required:
                - data
                - descendant_count_affected
            type: object
        RfidTag:
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. On this read schema the `tag_type` enum carries `x-extensible-enum: true` (an OpenAPI 3.0 vendor extension) to mark it open to additive variants; promoting that marker to native OpenAPI 3.1 enum semantics is a deferred migration.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            properties:
                id:
                    format: int64
                    maximum: 9007199254740991
                    readOnly: true
                    type: integer
                tag_type:
                    description: Discriminator for the polymorphic Tag resource. `rfid` denotes an RFID transponder, `ble` denotes a Bluetooth Low Energy beacon, and `barcode` denotes a 1D/2D barcode. Together with `value`, this forms the natural key — the same `value` may exist under different `tag_type`s without conflict.
                    enum:
                        - rfid
                    type: string
                    x-extensible-enum: true
                value:
                    maxLength: 255
                    minLength: 1
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
            required:
                - id
                - tag_type
                - value
            type: object
        RfidTagRequest:
            additionalProperties: false
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. The request `tag_type` enum is intentionally closed: it carries no `x-extensible-enum` marker, and a write naming an unrecognized variant is rejected as described above.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            properties:
                tag_type:
                    description: Discriminator for the polymorphic Tag resource. `rfid` denotes an RFID transponder, `ble` denotes a Bluetooth Low Energy beacon, and `barcode` denotes a 1D/2D barcode. Together with `value`, this forms the natural key — the same `value` may exist under different `tag_type`s without conflict.
                    enum:
                        - rfid
                    type: string
                value:
                    maxLength: 255
                    minLength: 1
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
            required:
                - tag_type
                - value
            type: object
        Tag:
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. On this read schema the `tag_type` enum carries `x-extensible-enum: true` (an OpenAPI 3.0 vendor extension) to mark it open to additive variants; promoting that marker to native OpenAPI 3.1 enum semantics is a deferred migration.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            discriminator:
                mapping:
                    barcode: '#/components/schemas/BarcodeTag'
                    ble: '#/components/schemas/BleTag'
                    rfid: '#/components/schemas/RfidTag'
                propertyName: tag_type
            oneOf:
                - $ref: '#/components/schemas/RfidTag'
                - $ref: '#/components/schemas/BleTag'
                - $ref: '#/components/schemas/BarcodeTag'
        TagRequest:
            description: "Polymorphic identifier attached to an asset or location, discriminated by `tag_type` into one of three variants: `RfidTag` (RFID transponder, `tag_type: rfid`), `BleTag` (Bluetooth Low Energy beacon, `tag_type: ble`), or `BarcodeTag` (1D/2D barcode, `tag_type: barcode`). The `/assets/{asset_id}/tags` and `/locations/{location_id}/tags` subresources accept and return all three variants; `tag_type` together with `value` form the natural key (a tag value is unique within its kind, not across kinds). \n\n`tag_type` is an **open** enumeration per the versioning policy — new variants may be added in additive minor revisions without a `/api/v2` cut. Integrators should treat unknown `tag_type` values as forward-compatible: pass the row through untouched rather than rejecting it. \n\n**Read direction is open; write direction is closed.** The open-enumeration semantic applies to **response handling**: if a `GET` returns a `tag_type` value your client doesn't recognize, pass the row through untouched rather than rejecting it (additive variants may appear in minor revisions). The **write surface is closed**: `POST /tags` and the natural-key tuple `(tag_type, value)` accept only the currently-defined variants (`rfid`, `ble`, `barcode`). Writes carrying an unrecognized `tag_type` are rejected with `400 validation_error` / `code: invalid_value` / `params.allowed_values: [...]`. The asymmetry is intentional — forward-compatibility on read, strict validation on write. The request `tag_type` enum is intentionally closed: it carries no `x-extensible-enum` marker, and a write naming an unrecognized variant is rejected as described above.\n\n**Codegen note:** some strict-typed generators (e.g. datamodel-codegen for Pydantic) materialize the single-value `tag_type` constants on each variant as separate enum classes — `TagType`, `TagType2`, `TagType4` or similar. That is a generator artifact and not part of the contract; the wire shape is a single discriminated union with three variants today. Treat the generated classes as implementation detail."
            discriminator:
                mapping:
                    barcode: '#/components/schemas/BarcodeTagRequest'
                    ble: '#/components/schemas/BleTagRequest'
                    rfid: '#/components/schemas/RfidTagRequest'
                propertyName: tag_type
            oneOf:
                - $ref: '#/components/schemas/RfidTagRequest'
                - $ref: '#/components/schemas/BleTagRequest'
                - $ref: '#/components/schemas/BarcodeTagRequest'
        TokenRequest:
            properties:
                client_id:
                    example: 6f1c2a8e-7d3b-4e90-9a11-2c4d5e6f7a8b
                    type: string
                client_secret:
                    example: trakrf_9f8e7d6c5b4a39281706f5e4d3c2b1a0ffeeddccbbaa99887766554433221100
                    type: string
                grant_type:
                    enum:
                        - client_credentials
                        - refresh_token
                    example: client_credentials
                    type: string
                refresh_token:
                    example: f3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
                    type: string
            required:
                - grant_type
            type: object
        TokenResponse:
            properties:
                access_token:
                    example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.short-lived-access-jwt
                    type: string
                expires_in:
                    example: 900
                    format: int32
                    type: integer
                refresh_token:
                    example: f3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
                    type: string
                token_type:
                    example: Bearer
                    type: string
            required:
                - access_token
                - refresh_token
                - token_type
                - expires_in
            type: object
        UpdateAssetRequest:
            properties:
                description:
                    example: Updated description
                    maxLength: 1024
                    minLength: 1
                    nullable: true
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
                is_active:
                    example: true
                    type: boolean
                metadata:
                    additionalProperties: {}
                    type: object
                name:
                    example: Forklift 3
                    maxLength: 255
                    minLength: 1
                    pattern: ^\S(?:[^\x00-\x1F\x7F]*\S)?$
                    type: string
                valid_from:
                    example: "2025-01-01T00:00:00Z"
                    format: date-time
                    type: string
                valid_to:
                    example: "2026-01-01T00:00:00Z"
                    format: date-time
                    nullable: true
                    type: string
            type: object
        UpdateAssetResponse:
            properties:
                data:
                    $ref: '#/components/schemas/AssetView'
            required:
                - data
            type: object
        UpdateLocationRequest:
            properties:
                description:
                    example: Updated description
                    maxLength: 1024
                    minLength: 1
                    nullable: true
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
                is_active:
                    example: true
                    type: boolean
                name:
                    example: Warehouse 1
                    maxLength: 255
                    minLength: 1
                    pattern: ^\S(?:[^\x00-\x1F\x7F]*\S)?$
                    type: string
                parent_external_key:
                    example: wh1
                    maxLength: 255
                    minLength: 1
                    nullable: true
                    pattern: ^[A-Za-z0-9-]+$
                    type: string
                parent_id:
                    example: 42
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    nullable: true
                    type: integer
                valid_from:
                    example: "2025-12-14T00:00:00Z"
                    format: date-time
                    type: string
                valid_to:
                    example: "2026-12-14T00:00:00Z"
                    format: date-time
                    nullable: true
                    type: string
            type: object
        UpdateLocationResponse:
            properties:
                data:
                    $ref: '#/components/schemas/LocationView'
            required:
                - data
            type: object
    securitySchemes:
        BearerAuth:
            bearerFormat: JWT
            description: 'TrakRF API access token. Format: "Bearer <access_token>". Create API credentials in the API Keys section of your TrakRF account to obtain a {client_id, client_secret} pair, then exchange them at POST /oauth/token (grant_type=client_credentials) for a short-lived access token; send that token here. Some OpenAPI generators (e.g. openapi-fetch, openapi-generator-cli python target) do not auto-attach the Authorization header from this scheme — set it manually if your generated client does not. Similarly, some generated clients (notably openapi-fetch) do not auto-attach `Content-Type: application/merge-patch+json` on PATCH operations, even though the operation''s requestBody.content lists only that media type; set the header manually per PATCH call if your client does not.'
            scheme: bearer
            type: http
info:
    contact:
        email: support@trakrf.id
        name: TrakRF Support
        url: https://trakrf.id
    description: |-
        TrakRF public REST API. See /api for the customer-facing reference.

        Spec available as YAML (/api/openapi.yaml) and JSON (/api/openapi.json).

        HTTP method coverage (HEAD, OPTIONS, 405 / Allow): /docs/api/http-method-coverage

        Nullable field interpretation: OpenAPI 3.0's `nullable: true` keyword is interpreted differently across codegen targets. Verified-working: `openapi-typescript@7.x` emits `string | null` correctly, and the `openapi-generator-cli` python target emits `Optional[StrictStr]` correctly — both round-trip CRUD against null-bearing responses unmodified. Known-broken: `datamodel-codegen@0.57.0` emits `nullable: true` read-shape fields as non-Optional required fields, so Pydantic validation fails on every nullable field that is actually null. Integrators using `datamodel-codegen` should either switch to the `openapi-generator-cli` python target or apply `--use-annotated --use-union-operator` with a custom post-processing pass.
    license:
        name: Business Source License 1.1
        url: https://github.com/trakrf/platform/blob/main/LICENSE
    title: TrakRF API
    version: 1.0.0
    x-package-name: trakrf-api-client
openapi: 3.0.3
paths:
    /api/v1/assets:
        get:
            description: |-
                **Required scope:** `assets:read`

                Paginated assets list with natural-key filters, sort, and substring search.

                Default scope returns currently-effective assets only — rows whose `valid_from` is in the past AND whose `valid_to` is null or in the future. The `is_active` filter is independent of temporal validity; omit it to include both active and inactive rows within the effective window, or pass `?is_active=true`/`false` to filter further.
            operationId: listAssets
            parameters:
                - description: max 200
                  in: query
                  name: limit
                  schema:
                    default: 50
                    maximum: 200
                    minimum: 1
                    type: integer
                - description: min 0
                  in: query
                  name: offset
                  schema:
                    default: 0
                    maximum: 2147483647
                    minimum: 0
                    type: integer
                - description: filter by asset external_key, equality match (may repeat for any-of)
                  explode: true
                  in: query
                  name: external_key
                  schema:
                    default: []
                    items:
                        pattern: ^[A-Za-z0-9-]+$
                        type: string
                    type: array
                  style: form
                - description: filter by active flag
                  in: query
                  name: is_active
                  schema:
                    type: boolean
                - description: when true, include soft-deleted rows in the response. deleted_at is populated for those rows. Orthogonal to is_active.
                  in: query
                  name: include_deleted
                  schema:
                    default: false
                    type: boolean
                - description: substring search (case-insensitive) on name, external_key, description, and active tag values
                  in: query
                  name: q
                  schema:
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
                - description: comma-separated; prefix '-' for DESC
                  explode: false
                  in: query
                  name: sort
                  schema:
                    default: ""
                    pattern: ^(|(?:external_key|-external_key|name|-name|created_at|-created_at|updated_at|-updated_at)(?:,(?:external_key|-external_key|name|-name|created_at|-created_at|updated_at|-updated_at))*)$
                    type: string
                  style: form
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListAssetsResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Bad Request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Not Found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Internal Server Error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: List assets
            tags:
                - assets
            x-required-scopes:
                - assets:read
        post:
            description: |-
                **Required scope:** `assets:write`

                Create a new asset record, optionally with one or more tags (RFID, BLE, barcode).

                The `external_key` field is optional. Provide a value from your system of record
                (ERP, WMS, asset management) for natural-key joins, or omit it to receive a
                server-assigned external_key in the format `ASSET-NNNN` (per-organization sequence).
                A caller-supplied external_key that collides with an existing asset returns 409.

                Returns the created asset with its assigned tags. The Location response header contains the path of the created resource (resolve against the request URL per RFC 7231 §7.1.2).
            operationId: createAsset
            requestBody:
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/CreateAssetWithTagsRequest'
                description: Asset to create with optional tags
                required: true
            responses:
                "201":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/CreateAssetResponse'
                    description: Created
                    headers:
                        Location:
                            description: Path of the created resource (resolve against request URL per RFC 7231 §7.1.2)
                            schema:
                                type: string
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Create an asset
            tags:
                - assets
            x-required-scopes:
                - assets:write
    /api/v1/assets/{asset_id}:
        delete:
            description: |-
                **Required scope:** `assets:write`

                Delete an asset by its canonical id. The asset is removed from all subsequent queries and its external_key becomes immediately available for reuse. Returns 204 on success, 404 if the asset does not exist or has already been deleted.
            operationId: deleteAsset
            parameters:
                - description: Asset id (canonical)
                  in: path
                  name: asset_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            responses:
                "204":
                    description: deleted
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Delete an asset
            tags:
                - assets
            x-required-scopes:
                - assets:write
        get:
            description: |-
                **Required scope:** `assets:read`

                Retrieve an asset by its canonical id. Returns 404 if the asset does not exist.

                Path-addressed retrieval bypasses the temporal-validity filter applied on list endpoints — any non-deleted asset is returned regardless of its `valid_from` / `valid_to` values. Use this endpoint when you have an id and need the row even if its effective window has elapsed.
            operationId: getAsset
            parameters:
                - description: Asset id (canonical)
                  in: path
                  name: asset_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetAssetResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Bad Request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Not Found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Internal Server Error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Get asset by canonical id
            tags:
                - assets
            x-required-scopes:
                - assets:read
        patch:
            description: |-
                **Required scope:** `assets:write`

                Apply a JSON Merge Patch (RFC 7396) to an asset. Only fields included in the request body are changed; fields set to `null` clear the corresponding nullable column. Omitted fields are left unchanged. Every accepted PATCH — empty body (`{}`), verbatim echo of current values, partial mutation, or full mutation — advances `updated_at` on success (filesystem `touch` semantics). Read-only fields are uniformly governed by the accept-if-matches, reject-if-differs rule: a value matching the current resource state is silently normalized out (so a verbatim GET → PATCH round-trip succeeds without manual scrubbing), and a differing value returns 400. The rejection `code` splits the two semantic classes: server-managed fields (`id`, `created_at`, `updated_at`, `deleted_at`) return `code: read_only` — they have no public mutation path. Fields mutable via a sub-resource verb (`external_key`, `tags`) return `code: invalid_context` and the detail names the correct verb: mutate `external_key` via POST /assets/{asset_id}/rename; mutate `tags` via POST /assets/{asset_id}/tags and DELETE /assets/{asset_id}/tags/{tag_id}. The `tags` collection is compared as a set on full tag content — array ordering is not significant; differing set membership or differing field values on a matching id returns 400 `invalid_context`. Asset location is not part of the asset resource — it is scan-derived fact data, read through GET /api/v1/reports/asset-locations or GET /api/v1/assets/{asset_id}/history; `location_id` / `location_external_key` in a request body are rejected 400 `read_only`.
            operationId: updateAsset
            parameters:
                - description: Asset id (canonical)
                  in: path
                  name: asset_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            requestBody:
                content:
                    application/merge-patch+json:
                        schema:
                            $ref: '#/components/schemas/UpdateAssetRequest'
                description: Fields to merge-patch
                required: true
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/UpdateAssetResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Update an asset
            tags:
                - assets
            x-required-scopes:
                - assets:write
    /api/v1/assets/{asset_id}/history:
        get:
            description: |-
                **Required scope:** `tracking:read`

                Location history for an asset identified by its canonical id.

                The asset existence check follows path-addressed semantics — the asset is returned even if its `valid_to` has elapsed. Each history row's location reference applies the temporal-validity predicate, so an event referencing a location whose effective window is past surfaces with null `location_external_key`.
            operationId: listAssetHistory
            parameters:
                - description: Asset id (canonical)
                  in: path
                  name: asset_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
                - description: max 200
                  in: query
                  name: limit
                  schema:
                    default: 50
                    maximum: 200
                    minimum: 1
                    type: integer
                - description: min 0
                  in: query
                  name: offset
                  schema:
                    default: 0
                    maximum: 2147483647
                    minimum: 0
                    type: integer
                - description: RFC 3339 start timestamp
                  in: query
                  name: from
                  schema:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    type: string
                - description: RFC 3339 end timestamp
                  in: query
                  name: to
                  schema:
                    example: "2025-04-29T12:34:56.000Z"
                    format: date-time
                    type: string
                - description: comma-separated; prefix '-' for DESC
                  explode: false
                  in: query
                  name: sort
                  schema:
                    default: ""
                    pattern: ^(|(?:event_observed_at|-event_observed_at)(?:,(?:event_observed_at|-event_observed_at))*)$
                    type: string
                  style: form
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AssetHistoryResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Bad Request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Not Found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Internal Server Error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Asset movement history
            tags:
                - assets
            x-required-scopes:
                - tracking:read
    /api/v1/assets/{asset_id}/rename:
        post:
            description: |-
                **Required scope:** `assets:write`

                Mutate the asset's `external_key` (natural / join key). This operation is **destructive to downstream joins**: any external system that has cached or indexed records on the old `external_key` will silently disconnect. Prefer a coordinated cutover with downstream consumers.

                `external_key` is immutable via PATCH; this operation is the only way to change it. Distinct from a regular PATCH in audit logs (different URL surface).

                The response includes `descendant_count_affected` for shape uniformity with the location rename verb. Assets have no hierarchy, so this value is always 0; it is emitted to save typed-client consumers from branching on rename verb.
            operationId: renameAsset
            parameters:
                - description: Asset id (canonical)
                  in: path
                  name: asset_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            requestBody:
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/RenameAssetRequest'
                description: New external_key
                required: true
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/RenameAssetResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Rename an asset (mutate external_key)
            tags:
                - assets
            x-required-scopes:
                - assets:write
    /api/v1/assets/{asset_id}/tags:
        post:
            description: |-
                **Required scope:** `assets:write`

                Attach a tag (RFID EPC, BLE beacon ID, barcode, etc.) to an existing asset.
                The tag must be unique within the organization.
            operationId: addAssetTag
            parameters:
                - description: Asset id (canonical)
                  in: path
                  name: asset_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            requestBody:
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/TagRequest'
                description: Tag to attach
                required: true
            responses:
                "201":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AddAssetTagResponse'
                    description: tag attached
                    headers:
                        Location:
                            description: Path of the created tag (resolve against request URL per RFC 7231 §7.1.2)
                            schema:
                                type: string
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Add a tag to an asset
            tags:
                - assets
            x-required-scopes:
                - assets:write
    /api/v1/assets/{asset_id}/tags/{tag_id}:
        delete:
            description: |-
                **Required scope:** `assets:write`

                Detach a tag from an asset by its tag record id.
                First successful removal returns 204; repeated calls return 404 — consistent with top-level resource DELETE semantics. The cross-asset / cross-org case (a tag that exists but is not attached to this asset, or belongs to a different org) also surfaces as 404.
            operationId: removeAssetTag
            parameters:
                - description: Asset id (canonical)
                  in: path
                  name: asset_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
                - description: Tag id
                  in: path
                  name: tag_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            responses:
                "204":
                    description: deleted
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Remove a tag from an asset
            tags:
                - assets
            x-required-scopes:
                - assets:write
    /api/v1/locations:
        get:
            description: |-
                **Required scope:** `locations:read`

                Paginated locations list with natural-key filters, sort, and substring search.

                Default scope returns currently-effective locations only — rows whose `valid_from` is in the past AND whose `valid_to` is null or in the future. The `is_active` filter is independent of temporal validity; omit it to include both active and inactive rows within the effective window, or pass `?is_active=true`/`false` to filter further.
            operationId: listLocations
            parameters:
                - description: max 200
                  in: query
                  name: limit
                  schema:
                    default: 50
                    maximum: 200
                    minimum: 1
                    type: integer
                - description: min 0
                  in: query
                  name: offset
                  schema:
                    default: 0
                    maximum: 2147483647
                    minimum: 0
                    type: integer
                - description: filter by parent id (canonical, may repeat); mutually exclusive with parent_external_key (400 ambiguous_fields if both supplied)
                  explode: true
                  in: query
                  name: parent_id
                  schema:
                    default: []
                    items:
                        format: int64
                        maximum: 9007199254740991
                        type: integer
                    type: array
                  style: form
                - description: filter by parent's external_key (may repeat); mutually exclusive with parent_id (400 ambiguous_fields if both supplied)
                  explode: true
                  in: query
                  name: parent_external_key
                  schema:
                    default: []
                    items:
                        pattern: ^[A-Za-z0-9-]+$
                        type: string
                    type: array
                  style: form
                - description: filter by location external_key, equality match (may repeat for any-of)
                  explode: true
                  in: query
                  name: external_key
                  schema:
                    default: []
                    items:
                        pattern: ^[A-Za-z0-9-]+$
                        type: string
                    type: array
                  style: form
                - description: filter by active flag
                  in: query
                  name: is_active
                  schema:
                    type: boolean
                - description: when true, include soft-deleted rows in the response. deleted_at is populated for those rows. Orthogonal to is_active.
                  in: query
                  name: include_deleted
                  schema:
                    default: false
                    type: boolean
                - description: substring search (case-insensitive) on name, external_key, description, and active tag values
                  in: query
                  name: q
                  schema:
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
                - description: comma-separated, prefix '-' for DESC
                  explode: false
                  in: query
                  name: sort
                  schema:
                    default: ""
                    pattern: ^(|(?:external_key|-external_key|name|-name|created_at|-created_at)(?:,(?:external_key|-external_key|name|-name|created_at|-created_at))*)$
                    type: string
                  style: form
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListLocationsResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: List locations
            tags:
                - locations
            x-required-scopes:
                - locations:read
        post:
            description: |-
                **Required scope:** `locations:write`

                Create a new location in the hierarchy, optionally with one or more tags.
                Set parent_id (canonical) or parent_external_key (alternate) to nest under an existing parent.
                Both forms may be supplied together when they name the same parent (silently normalized to a single re-parent operation); a disagreement returns 400 `ambiguous_fields`.

                The `external_key` field is optional. Provide a value from your system of record
                (ERP, WMS, layout/plan) for natural-key joins, or omit it to receive a
                server-assigned external_key in the format `LOC-NNN` (per-organization
                sequence, 3-digit zero-pad, grows naturally beyond 999).
                A caller-supplied external_key that collides with an existing location returns 409.
            operationId: createLocation
            requestBody:
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/CreateLocationWithTagsRequest'
                description: Location to create with optional tags
                required: true
            responses:
                "201":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/CreateLocationResponse'
                    description: Created
                    headers:
                        Location:
                            description: Path of the created resource (resolve against request URL per RFC 7231 §7.1.2)
                            schema:
                                type: string
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Create a location
            tags:
                - locations
            x-required-scopes:
                - locations:write
    /api/v1/locations/{location_id}:
        delete:
            description: |-
                **Required scope:** `locations:write`

                Delete a location by its ID. Returns 204 on success, 404 if the location does not exist or has already been deleted, and 409 if the location has descendant locations or assets placed directly at it. Descendants must be reassigned or removed and placed assets must be moved or removed before their parent location can be deleted; bulk cascade is not supported.
            operationId: deleteLocation
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            responses:
                "204":
                    description: deleted
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict — has descendants or placed assets
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Delete location
            tags:
                - locations
            x-required-scopes:
                - locations:write
        get:
            description: |-
                **Required scope:** `locations:read`

                Retrieve a location by its canonical ID. Returns 404 if not found.

                Path-addressed retrieval bypasses the temporal-validity filter applied on list endpoints — any non-deleted location is returned regardless of its `valid_from` / `valid_to` values. Use this endpoint when you have an id and need the row even if its effective window has elapsed.
            operationId: getLocation
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetLocationResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Bad Request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Not Found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Too Many Requests
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Get location by ID
            tags:
                - locations
            x-required-scopes:
                - locations:read
        patch:
            description: |-
                **Required scope:** `locations:write`

                Apply a JSON Merge Patch (RFC 7396) to a location. Only fields included in the request body are changed; fields set to `null` clear the corresponding nullable column. Omitted fields are left unchanged. Every accepted PATCH — empty body (`{}`), verbatim echo of current values, partial mutation, or full mutation — advances `updated_at` on success (filesystem `touch` semantics). Read-only fields are uniformly governed by the accept-if-matches, reject-if-differs rule: a value matching the current resource state is silently normalized out (so a verbatim GET → PATCH round-trip succeeds without manual scrubbing), and a differing value returns 400. The rejection `code` splits the two semantic classes: server-managed fields (`id`, `created_at`, `updated_at`, `deleted_at`) return `code: read_only` — they have no public mutation path. Fields mutable via a sub-resource verb (`external_key`, `tags`) return `code: invalid_context` and the detail names the correct verb: mutate `external_key` via POST /locations/{location_id}/rename; mutate `tags` via POST /locations/{location_id}/tags and DELETE /locations/{location_id}/tags/{tag_id}. The `tags` collection is compared as a set on full tag content — array ordering is not significant; differing set membership or differing field values on a matching id returns 400 `invalid_context`. To re-parent, send `parent_id` (surrogate) OR `parent_external_key` (natural key), or both forms in the same body provided they resolve to the same parent (silently normalized to a single re-parent operation, symmetric with CreateLocationRequest); either form accepts `null` to clear the FK, and disagreement between the two forms returns 400 `ambiguous_fields`.
            operationId: updateLocation
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            requestBody:
                content:
                    application/merge-patch+json:
                        schema:
                            $ref: '#/components/schemas/UpdateLocationRequest'
                description: Fields to merge-patch
                required: true
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/UpdateLocationResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Update a location
            tags:
                - locations
            x-required-scopes:
                - locations:write
    /api/v1/locations/{location_id}/ancestors:
        get:
            description: |-
                **Required scope:** `locations:read`

                Sort order is fixed: ancestors are returned root first (walking up the `parent_id` chain), with `id` ascending as a deterministic tiebreaker. No `sort` query parameter is exposed because the natural order toward the root is the only meaningful order for this list.
            operationId: listLocationAncestors
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
                - description: max 200
                  in: query
                  name: limit
                  schema:
                    default: 50
                    maximum: 200
                    minimum: 1
                    type: integer
                - description: min 0
                  in: query
                  name: offset
                  schema:
                    default: 0
                    maximum: 2147483647
                    minimum: 0
                    type: integer
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListLocationAncestorsResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: List location ancestors
            tags:
                - locations
            x-required-scopes:
                - locations:read
    /api/v1/locations/{location_id}/children:
        get:
            description: |-
                **Required scope:** `locations:read`

                Sort order is fixed: immediate children are returned ordered alphabetically by `name` ascending, with `id` ascending as a deterministic tiebreaker when sibling names collide. No `sort` query parameter is exposed because alphabetical-by-name is the only meaningful order for a single level of siblings.
            operationId: listLocationChildren
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
                - description: max 200
                  in: query
                  name: limit
                  schema:
                    default: 50
                    maximum: 200
                    minimum: 1
                    type: integer
                - description: min 0
                  in: query
                  name: offset
                  schema:
                    default: 0
                    maximum: 2147483647
                    minimum: 0
                    type: integer
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListLocationChildrenResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: List location children
            tags:
                - locations
            x-required-scopes:
                - locations:read
    /api/v1/locations/{location_id}/descendants:
        get:
            description: |-
                **Required scope:** `locations:read`

                Sort order is fixed: descendants are returned in depth-first tree order (each level sorted by lowercased `external_key`), with `id` ascending as a deterministic tiebreaker. No `sort` query parameter is exposed because the depth-first tree walk is the only meaningful order for this list.
            operationId: listLocationDescendants
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
                - description: max 200
                  in: query
                  name: limit
                  schema:
                    default: 50
                    maximum: 200
                    minimum: 1
                    type: integer
                - description: min 0
                  in: query
                  name: offset
                  schema:
                    default: 0
                    maximum: 2147483647
                    minimum: 0
                    type: integer
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListLocationDescendantsResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: List location descendants
            tags:
                - locations
            x-required-scopes:
                - locations:read
    /api/v1/locations/{location_id}/rename:
        post:
            description: |-
                **Required scope:** `locations:write`

                Mutate the location's `external_key`. This operation is **destructive to downstream joins** because consumers of the natural key must re-resolve it. Only this row's `external_key` changes on the server; descendants are not modified.

                The response includes `descendant_count_affected` (the live descendant count reachable through `parent_id`) so an integrator can decide whether to refresh derived natural-key joins for the subtree. `external_key` is immutable via PATCH; this operation is the only way to change it. Distinct from a regular PATCH in audit logs (different URL surface).
            operationId: renameLocation
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            requestBody:
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/RenameLocationRequest'
                description: New external_key
                required: true
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/RenameLocationResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Rename a location (mutate external_key)
            tags:
                - locations
            x-required-scopes:
                - locations:write
    /api/v1/locations/{location_id}/tags:
        post:
            description: '**Required scope:** `locations:write`'
            operationId: addLocationTag
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            requestBody:
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/TagRequest'
                description: Tag to attach
                required: true
            responses:
                "201":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AddLocationTagResponse'
                    description: tag attached
                    headers:
                        Location:
                            description: Path of the created tag (resolve against request URL per RFC 7231 §7.1.2)
                            schema:
                                type: string
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "409":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: conflict
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Add a tag to a location
            tags:
                - locations
            x-required-scopes:
                - locations:write
    /api/v1/locations/{location_id}/tags/{tag_id}:
        delete:
            description: |-
                **Required scope:** `locations:write`

                Detach a tag from a location by its tag record id.
                First successful removal returns 204; repeated calls return 404 — consistent with top-level resource DELETE semantics. The cross-location / cross-org case (a tag that exists but is not attached to this location, or belongs to a different org) also surfaces as 404.
            operationId: removeLocationTag
            parameters:
                - description: Location ID
                  in: path
                  name: location_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
                - description: Tag ID
                  in: path
                  name: tag_id
                  required: true
                  schema:
                    format: int64
                    maximum: 9007199254740991
                    minimum: 1
                    type: integer
            responses:
                "204":
                    description: deleted
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: internal_error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Remove a tag from a location
            tags:
                - locations
            x-required-scopes:
                - locations:write
    /api/v1/oauth/token:
        post:
            description: 'Exchange API credentials for a short-lived (15 min) access token + a rotating 30-day refresh token. Two grants are supported. `client_credentials`: supply `client_id` and `client_secret` (the opaque {client_id, client_secret} pair returned once at API-key creation). `refresh_token`: supply a current `refresh_token` to rotate the pair; presenting an already-used refresh token revokes the whole chain and returns 401.'
            operationId: createToken
            requestBody:
                content:
                    application/json:
                        schema:
                            $ref: '#/components/schemas/TokenRequest'
                    application/x-www-form-urlencoded:
                        schema:
                            $ref: '#/components/schemas/TokenRequest'
                description: Token grant request
                required: true
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/TokenResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Validation error / unsupported grant_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Invalid client credentials or refresh token
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "415":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: unsupported_media_type
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            summary: OAuth2 token grant
            tags:
                - oauth
            x-required-scopes: []
    /api/v1/orgs/me:
        get:
            description: Returns the organization scoped by the API key presented in Authorization. Intended as a lightweight health-check primitive for integrators verifying a key works end-to-end. Singleton endpoint; accepts no query parameters. Any query parameter returns `400 validation_error / unknown_field`.
            operationId: getCurrentOrg
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetCurrentOrgResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: bad_request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: not_found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "422":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: missing_org_context — the API key authenticated but its org no longer exists
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Internal server error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: Get the org associated with the authenticated API key
            tags:
                - orgs
            x-required-scopes: []
    /api/v1/reports/asset-locations:
        get:
            description: |-
                **Required scope:** `tracking:read`

                Snapshot of each asset's most recent location, filterable by either side of the join. Filter by location (`location_id` / `location_external_key`) to retrieve everything currently at a place; filter by asset (`asset_id` / `asset_external_key`, repeatable) to resolve a batch of assets from a master system to their current locations in one round-trip. Within each pair the surrogate and natural-key forms are mutually exclusive (400 `ambiguous_fields` if both are supplied); the asset and location filter pairs are independent and intersect when combined. Because this view is derived from immutable scan history, it can resolve references for assets that have since been deleted. By default those rows are excluded; pass `include_deleted=true` to include them, and check `asset_deleted_at` to distinguish deleted from live.

                Rows are produced from `scan_event` history and reflect the most recent observed location per asset. **Assets that have never been scanned do not appear in this report** — they exist in `/api/v1/assets` but have no derived location row until at least one scan event has been observed, so this endpoint's `total_count` can lag `/api/v1/assets` `total_count` for newly-onboarded inventory. Use `/api/v1/assets` directly if you need a complete asset roster including never-scanned assets.

                Temporal validity is applied to both joined entities. Assets whose effective window is past or future are excluded entirely. Locations whose effective window is past or future surface with null `location_id` / `location_external_key` while the parent asset row remains visible. Soft-deleted locations are projected the same way here — null on the report row — even though the identifier still lives on the location row; reports endpoints intentionally hide tombstoned anchor points from scan-derived summaries. Use the locations endpoint with `include_deleted=true` to retrieve the underlying identifier.
            operationId: listAssetLocations
            parameters:
                - description: max 200
                  in: query
                  name: limit
                  schema:
                    default: 50
                    maximum: 200
                    minimum: 1
                    type: integer
                - description: min 0
                  in: query
                  name: offset
                  schema:
                    default: 0
                    maximum: 2147483647
                    minimum: 0
                    type: integer
                - description: filter by location id (canonical, may repeat); mutually exclusive with location_external_key (400 ambiguous_fields if both supplied)
                  explode: true
                  in: query
                  name: location_id
                  schema:
                    default: []
                    items:
                        format: int64
                        maximum: 9007199254740991
                        type: integer
                    type: array
                  style: form
                - description: filter by location external_key (may repeat); mutually exclusive with location_id (400 ambiguous_fields if both supplied)
                  explode: true
                  in: query
                  name: location_external_key
                  schema:
                    default: []
                    items:
                        pattern: ^[A-Za-z0-9-]+$
                        type: string
                    type: array
                  style: form
                - description: filter by asset id (canonical, may repeat); mutually exclusive with asset_external_key (400 ambiguous_fields if both supplied)
                  explode: true
                  in: query
                  name: asset_id
                  schema:
                    default: []
                    items:
                        format: int64
                        maximum: 9007199254740991
                        type: integer
                    type: array
                  style: form
                - description: filter by asset external_key (may repeat); mutually exclusive with asset_id (400 ambiguous_fields if both supplied)
                  explode: true
                  in: query
                  name: asset_external_key
                  schema:
                    default: []
                    items:
                        pattern: ^[A-Za-z0-9-]+$
                        type: string
                    type: array
                  style: form
                - description: substring search (case-insensitive) on asset name, external_key, and active tag values
                  in: query
                  name: q
                  schema:
                    pattern: ^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]*$
                    type: string
                - description: include rows for soft-deleted assets
                  in: query
                  name: include_deleted
                  schema:
                    default: false
                    type: boolean
                - description: comma-separated sort fields; prefix '-' for DESC
                  explode: false
                  in: query
                  name: sort
                  schema:
                    default: ""
                    pattern: ^(|(?:asset_last_seen|-asset_last_seen|asset_external_key|-asset_external_key|location_external_key|-location_external_key)(?:,(?:asset_last_seen|-asset_last_seen|asset_external_key|-asset_external_key|location_external_key|-location_external_key))*)$
                    type: string
                  style: form
            responses:
                "200":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/AssetLocationsResponse'
                    description: OK
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "400":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Bad Request
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "401":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unauthorized
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        WWW-Authenticate:
                            $ref: '#/components/headers/WWWAuthenticate'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "403":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Forbidden
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "404":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Not Found
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "405":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Method not allowed
                    headers:
                        Allow:
                            $ref: '#/components/headers/Allow'
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "429":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: rate_limited
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        Retry-After:
                            $ref: '#/components/headers/RetryAfter'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                "500":
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Internal Server Error
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
                default:
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ErrorResponse'
                    description: Unmodeled error. Generated clients should treat any response not otherwise enumerated as a structured ErrorResponse.
                    headers:
                        RateLimit-Policy:
                            $ref: '#/components/headers/RateLimitPolicy'
                        X-RateLimit-Limit:
                            $ref: '#/components/headers/XRateLimitLimit'
                        X-RateLimit-Remaining:
                            $ref: '#/components/headers/XRateLimitRemaining'
                        X-RateLimit-Reset:
                            $ref: '#/components/headers/XRateLimitReset'
                        X-Request-Id:
                            $ref: '#/components/headers/XRequestId'
            security:
                - BearerAuth: []
            summary: List current asset locations
            tags:
                - reports
            x-required-scopes:
                - tracking:read
security:
    - BearerAuth: []
servers:
    - description: Preview (per-PR deploys). Preview-scoped API keys authenticate here only — they will fail with 401 against Production, and Production keys will fail here.
      url: https://app.preview.trakrf.id
    - description: Production. Production-scoped API keys authenticate here only — they will fail with 401 against Preview, and Preview keys will fail here.
      url: https://app.trakrf.id
tags:
    - description: Asset CRUD, history, and tag membership.
      name: assets
    - description: Location CRUD, hierarchy traversal, and tag membership.
      name: locations
    - description: Caller's organization context.
      name: orgs
    - description: Cross-cutting reporting endpoints (asset locations, etc.).
      name: reports
