{
  "openapi": "3.1.0",
  "info": {
    "title": "STH API",
    "version": "1.0.0",
    "description": "API to reach STH programmatically. Authenticate with a Bearer API key."
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key obtained from AssetGym Settings > API Keys"
      }
    },
    "schemas": {
      "VaultFileListResponse": {
        "type": "object",
        "properties": {
          "data": {
            "$ref": "#/components/schemas/VaultFileList"
          }
        },
        "required": [
          "data"
        ]
      },
      "VaultFileList": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/VaultFile"
        }
      },
      "VaultFile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Short UUID of the file",
            "example": "mMxzWbBDSfiEP6MBcfNUgP"
          },
          "name": {
            "type": "string",
            "description": "Original file name",
            "example": "report.pdf"
          },
          "mime_type": {
            "type": "string",
            "description": "MIME type",
            "example": "application/pdf"
          },
          "size_bytes": {
            "type": "integer",
            "description": "File size in bytes",
            "example": 102400
          },
          "last_downloaded_at": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 8601 timestamp of last download",
            "example": "2026-04-20T10:30:00.000Z"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 creation timestamp",
            "example": "2026-04-15T08:00:00.000Z"
          },
          "download_url": {
            "type": "string",
            "format": "uri",
            "description": "Short-lived presigned URL to download the file. Valid until `download_url_expires_at`.",
            "example": "https://s3.eu-west-1.amazonaws.com/bucket/asset-vault/user-123/file-456/upload.pdf?X-Amz-Signature=..."
          },
          "download_url_expires_at": {
            "type": "string",
            "description": "ISO 8601 UTC timestamp when `download_url` stops working.",
            "example": "2026-04-29T13:30:00.000Z"
          }
        },
        "required": [
          "id",
          "name",
          "mime_type",
          "size_bytes",
          "last_downloaded_at",
          "created_at",
          "download_url",
          "download_url_expires_at"
        ]
      },
      "ApiKeyMissingError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "api_key_missing"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ApiKeyInvalidError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "api_key_invalid"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "PermissionDeniedError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "permission_denied"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "RateLimitExceededError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "rate_limit_exceeded"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "InternalErrorError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "internal_error"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "validation_failed"
                ]
              },
              "message": {
                "type": "string",
                "example": "Request validation failed"
              },
              "issues": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ErrorIssue"
                }
              }
            },
            "required": [
              "code",
              "message",
              "issues"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ErrorIssue": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "Dotted path to the offending field, including array indices (e.g. `addon_types.0`). Empty string for top-level errors.",
            "example": "asset_type_code"
          },
          "code": {
            "type": "string",
            "enum": [
              "required",
              "invalid_type",
              "invalid_value",
              "out_of_range",
              "custom"
            ],
            "description": "Closed-set programmatic identifier for the issue type.",
            "example": "required"
          },
          "message": {
            "type": "string",
            "description": "Human-readable description of the issue.",
            "example": "Required"
          }
        },
        "required": [
          "path",
          "code",
          "message"
        ]
      },
      "UnsupportedMediaTypeError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unsupported_media_type"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "CatalogSuggestResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogSuggestion"
            },
            "description": "Ranked list of suggestions. Capped to at most 5 entries."
          }
        },
        "required": [
          "data"
        ]
      },
      "CatalogSuggestion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the underlying entity. When `entity_type` = 'asset' this is the `asset_id`; when 'taxonomy' it is the `taxonomy_id`.",
            "example": 1234
          },
          "entity_type": {
            "type": "string",
            "enum": [
              "taxonomy",
              "asset"
            ],
            "description": "`taxonomy` matches any taxonomy level; `asset` matches manufacturers and individual assets.",
            "example": "asset"
          },
          "display_name": {
            "type": "string",
            "description": "Friendly display string for the row. For asset (leaf) rows this is the full concatenated chain (e.g. `\"Caterpillar 320 GC\"`); for taxonomy and manufacturer rows it is the bare entity name (e.g. `\"Excavators\"`, `\"Caterpillar\"`).",
            "example": "Caterpillar 320 GC"
          },
          "asset_chain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogChainNode"
            },
            "description": "Chain through the assets tree ordered from the root down to the row itself: manufacturer first, the row last (e.g. `[\"Komatsu\", \"PC\", \"210-7\"]`). Always `[]` for taxonomy rows (their chain is in `taxonomy_chain`)."
          },
          "taxonomy_chain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogChainNode"
            },
            "description": "Chain through the taxonomy tree ordered from the root down: broadest category first, leaf last. For taxonomy rows this is the row's own chain (ending in the row itself); for asset rows it is the classification chain, populated only when the asset carries its own taxonomy."
          }
        },
        "required": [
          "id",
          "entity_type",
          "display_name",
          "asset_chain",
          "taxonomy_chain"
        ]
      },
      "CatalogChainNode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the node (asset_id or taxonomy_id).",
            "example": 100
          },
          "name": {
            "type": "string",
            "description": "Name of the chain node.",
            "example": "Caterpillar"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "CatalogSuggestRequest": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Free-text term that is being searched.",
            "example": "komatsu pc210"
          },
          "entity_type": {
            "type": "string",
            "enum": [
              "taxonomy",
              "asset"
            ],
            "description": "Restrict results to a single domain. Omit to return both.",
            "example": "asset"
          },
          "parent_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647,
            "description": "Scope to the subtree below this asset. By default every descendant is searched; set `direct_only=true` to search only the direct children. Cannot be combined with `entity_type=\"taxonomy\"` (taxonomy rows are not part of the assets tree).",
            "example": 1234
          },
          "taxonomy_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 2147483647,
            "description": "Classification scope. Asset and manufacturer rows match when anything in their subtree is classified at or under this taxonomy. Taxonomy rows match when they are this taxonomy or one of its descendants; or, with `direct_only=true` (and no `parent_id`), exactly its direct child taxonomies.",
            "example": 56
          },
          "direct_only": {
            "type": "boolean",
            "description": "Search only the first level of the scope. With `parent_id`: the direct children of that asset. With `taxonomy_id` (and no `parent_id`): its direct child taxonomies, plus manufacturers whose subtree intersects the taxonomy. With no scope: tree roots only (industries and manufacturers). Defaults to `false` (full depth).",
            "example": false
          }
        },
        "required": [
          "query"
        ],
        "additionalProperties": false
      },
      "CatalogTaxonomyListResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogTaxonomyNode"
            },
            "description": "Page of taxonomy nodes."
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Total count after filters, before paging. Only present when `include_total=true` was passed.",
            "example": 14
          }
        },
        "required": [
          "data"
        ]
      },
      "CatalogTaxonomyNode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the taxonomy node.",
            "example": 12
          },
          "name": {
            "type": "string",
            "description": "Name of the taxonomy.",
            "example": "Excavators"
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "description": "Direct parent id, or `null` for a root taxonomy.",
            "example": 3
          }
        },
        "required": [
          "id",
          "name",
          "parent_id"
        ]
      },
      "NotFoundError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "not_found"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "CatalogAssetListResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogAssetNode"
            },
            "description": "Page of asset nodes."
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Total count after filters, before paging. Only present when `include_total=true` was passed.",
            "example": 42
          }
        },
        "required": [
          "data"
        ]
      },
      "CatalogAssetNode": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the asset node.",
            "example": 1234
          },
          "name": {
            "type": "string",
            "description": "Name of the asset.",
            "example": "210-7"
          },
          "qualified_name": {
            "type": "string",
            "description": "Name prefixed with all ancestor names, space-separated (e.g. `Komatsu PC 210-7`).",
            "example": "Komatsu PC 210-7"
          },
          "parent_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "description": "Direct parent asset id, or `null` for a manufacturer.",
            "example": 100
          }
        },
        "required": [
          "id",
          "name",
          "qualified_name",
          "parent_id"
        ]
      },
      "IdentifyResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IdentificationMatch"
            },
            "description": "Potential matches across all provided inputs, ranked best first: matches supported by more inputs rank above single-input matches, and deterministic serial decodes rank above probabilistic description matches. Empty when nothing matched. Each match identifies the asset axis, the taxonomy axis, or both. Capped to at most 5 entries."
          },
          "serial_decode": {
            "$ref": "#/components/schemas/SerialNumberDecodeResult"
          },
          "photo": {
            "$ref": "#/components/schemas/PhotoReading"
          }
        },
        "required": [
          "data"
        ]
      },
      "IdentificationMatch": {
        "type": "object",
        "properties": {
          "asset_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "description": "Id of the identified node in the assets tree, or `null` when the evidence did not resolve to anything on the asset axis. Can be any level of the tree: a manufacturer when only the maker was recognized, or a model when the match is precise. At least one of `asset_id` and `taxonomy_id` is set.",
            "example": 36988
          },
          "taxonomy_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "description": "Id of the identified taxonomy node, or `null` when the kind of machine could not be determined. At least one of `asset_id` and `taxonomy_id` is set.",
            "example": 1063
          },
          "asset_display_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Friendly display string for the asset axis: the full concatenated asset chain (e.g. `\"Komatsu PC 210LC-8\"`, or just `\"Komatsu\"` for a manufacturer-level match). `null` when `asset_id` is `null`.",
            "example": "Komatsu PC 210LC-8"
          },
          "taxonomy_display_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Friendly display string for the taxonomy axis: the bare taxonomy name (e.g. `\"Crawler Excavator\"`). `null` when `taxonomy_id` is `null`.",
            "example": "76-130 KW"
          },
          "asset_chain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogChainNode"
            },
            "description": "Chain through the assets tree ordered from the root down: the manufacturer first, the identified node last. `[]` when `asset_id` is `null`."
          },
          "taxonomy_chain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogChainNode"
            },
            "description": "Chain through the taxonomy tree ordered from the root down: the broadest category first, the identified taxonomy last. `[]` when `taxonomy_id` is `null`."
          }
        },
        "required": [
          "asset_id",
          "taxonomy_id",
          "asset_display_name",
          "taxonomy_display_name",
          "asset_chain",
          "taxonomy_chain"
        ]
      },
      "SerialNumberDecodeResult": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/SerialNumberDecoded"
          },
          {
            "$ref": "#/components/schemas/SerialNumberModelNotInCatalog"
          },
          {
            "$ref": "#/components/schemas/SerialNumberAmbiguousModel"
          },
          {
            "$ref": "#/components/schemas/SerialNumberUnknownModel"
          },
          {
            "$ref": "#/components/schemas/SerialNumberChecksumFailed"
          },
          {
            "$ref": "#/components/schemas/SerialNumberUnrecognizedFormat"
          },
          {
            "$ref": "#/components/schemas/SerialNumberAmbiguousManufacturer"
          },
          {
            "$ref": "#/components/schemas/SerialNumberNoMatch"
          },
          {
            "$ref": "#/components/schemas/SerialNumberUnsupportedManufacturer"
          },
          {
            "$ref": "#/components/schemas/SerialNumberDecodeFailure"
          }
        ],
        "discriminator": {
          "propertyName": "status",
          "mapping": {
            "decoded": "#/components/schemas/SerialNumberDecoded",
            "model_not_in_catalog": "#/components/schemas/SerialNumberModelNotInCatalog",
            "ambiguous_model": "#/components/schemas/SerialNumberAmbiguousModel",
            "unknown_model": "#/components/schemas/SerialNumberUnknownModel",
            "checksum_failed": "#/components/schemas/SerialNumberChecksumFailed",
            "unrecognized_format": "#/components/schemas/SerialNumberUnrecognizedFormat",
            "ambiguous_manufacturer": "#/components/schemas/SerialNumberAmbiguousManufacturer",
            "no_match": "#/components/schemas/SerialNumberNoMatch",
            "unsupported_manufacturer": "#/components/schemas/SerialNumberUnsupportedManufacturer",
            "error": "#/components/schemas/SerialNumberDecodeFailure"
          }
        },
        "description": "How the serial was decoded, in the same shape as `POST /catalog/serial-numbers/decode`. Present exactly when a serial was available: the `serial_number` field when sent, otherwise the serial read off the photo; its own `serial_number` says which. Absent when neither input carried a serial. The serial-based entries in `data` derive from it: a `decoded` result gives its asset, a manufacturer-level result (`model_not_in_catalog`, `unknown_model`, `checksum_failed`, `unrecognized_format`) gives the manufacturer, an `ambiguous_model` result gives its candidates (or what they share when the catalogue has none of them), and `ambiguous_manufacturer`, `no_match`, `unsupported_manufacturer` and `error` give nothing. A tie's readings are in its `alternatives`; send `manufacturer_id` to resolve it."
      },
      "SerialNumberDecoded": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "decoded"
            ],
            "description": "The serial names exactly one asset the catalogue has.",
            "example": "decoded"
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The year encoded in the serial (the decoder does not say whether it is a model year or a build year), or `null` when the serial encodes none or its year code is unknown.",
            "example": 2021
          },
          "asset": {
            "$ref": "#/components/schemas/CatalogAssetReference"
          }
        },
        "required": [
          "serial_number",
          "status",
          "year",
          "asset"
        ]
      },
      "CatalogAssetReference": {
        "type": "object",
        "properties": {
          "asset_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the node in the assets tree. Any level: a manufacturer when only the maker is known, a family node for a shared ancestor, a model for a precise decode. The stable handle to store and compare.",
            "example": 36988
          },
          "asset_display_name": {
            "type": "string",
            "description": "Friendly display string: the full concatenated asset chain (e.g. `\"Komatsu PC 210LC-8\"`, or just `\"Komatsu\"` for a manufacturer).",
            "example": "Komatsu PC 210LC-8"
          },
          "asset_chain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogChainNode"
            },
            "description": "Chain through the assets tree ordered from the root down: the manufacturer first, this asset last."
          },
          "taxonomy_id": {
            "type": [
              "integer",
              "null"
            ],
            "exclusiveMinimum": 0,
            "description": "Id of the asset's own taxonomy node, or `null` when it carries none (manufacturers never do; a few models don't). Not inherited from parents.",
            "example": 1063
          },
          "taxonomy_display_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Friendly display string of the taxonomy node. `null` when `taxonomy_id` is `null`.",
            "example": "76-130 KW"
          },
          "taxonomy_chain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogChainNode"
            },
            "description": "Chain through the taxonomy tree ordered from the root down: the broadest category first, the asset's taxonomy last. `[]` when `taxonomy_id` is `null`."
          }
        },
        "required": [
          "asset_id",
          "asset_display_name",
          "asset_chain",
          "taxonomy_id",
          "taxonomy_display_name",
          "taxonomy_chain"
        ],
        "description": "The identified asset."
      },
      "SerialNumberModelNotInCatalog": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "model_not_in_catalog"
            ],
            "description": "The serial names a model the catalogue does not have yet. The manufacturer is certain; use it, and read the model from `decoded_name_path`.",
            "example": "model_not_in_catalog"
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The year encoded in the serial (the decoder does not say whether it is a model year or a build year), or `null` when the serial encodes none or its year code is unknown.",
            "example": 2021
          },
          "asset": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "The manufacturer: the deepest catalogue asset the serial reaches."
              }
            ]
          },
          "decoded_name_path": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 2,
            "description": "The decoder's own name path for what the serial names, manufacturer first. Fixed when the decoding rules were written, so it is a label, not a catalogue name.",
            "example": [
              "Case IH",
              "Luxxum 100"
            ]
          }
        },
        "required": [
          "serial_number",
          "status",
          "year",
          "asset",
          "decoded_name_path"
        ]
      },
      "SerialNumberAmbiguousModel": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "ambiguous_model"
            ],
            "description": "The serial names one manufacturer and one of several models (a type code shared across models).",
            "example": "ambiguous_model"
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The year encoded in the serial (the decoder does not say whether it is a model year or a build year), or `null` when the serial encodes none or its year code is unknown.",
            "example": 2021
          },
          "common_ancestor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "What every candidate agrees on: the deepest catalogue asset all of them descend from (a family node, or the manufacturer). The safe attribution."
              }
            ]
          },
          "candidates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogAssetReference"
            },
            "description": "The assets the serial may name, each a descendant of `common_ancestor`. Narrow them with other evidence. Can be shorter than the decoder's own list when the catalogue lacks some of the models, even empty."
          }
        },
        "required": [
          "serial_number",
          "status",
          "year",
          "common_ancestor",
          "candidates"
        ]
      },
      "SerialNumberUnknownModel": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "unknown_model"
            ],
            "description": "The format matched and every position was read, but the model code is not in the decoder's rules. The manufacturer and the year are certain; the serial is fine and the rules are incomplete.",
            "example": "unknown_model"
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The year encoded in the serial (the decoder does not say whether it is a model year or a build year), or `null` when the serial encodes none or its year code is unknown.",
            "example": 2021
          },
          "asset": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "The manufacturer."
              }
            ]
          }
        },
        "required": [
          "serial_number",
          "status",
          "year",
          "asset"
        ]
      },
      "SerialNumberChecksumFailed": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "checksum_failed"
            ],
            "description": "The format matched, but the serial fails its own check character, so it is corrupt or misread and nothing it encodes counts as evidence. Only the manufacturer the format proves is reported; distrust the serial.",
            "example": "checksum_failed"
          },
          "asset": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "The manufacturer."
              }
            ]
          }
        },
        "required": [
          "serial_number",
          "status",
          "asset"
        ]
      },
      "SerialNumberUnrecognizedFormat": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "unrecognized_format"
            ],
            "description": "The serial opens on a prefix that proves the manufacturer (a WMI, a factory prefix), but none of that manufacturer's formats accepts it. Nothing past the prefix was read; the serial may be corrupt or its format not covered yet.",
            "example": "unrecognized_format"
          },
          "asset": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "The manufacturer."
              }
            ]
          }
        },
        "required": [
          "serial_number",
          "status",
          "asset"
        ]
      },
      "SerialNumberAmbiguousManufacturer": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "ambiguous_manufacturer"
            ],
            "description": "Several manufacturers' formats accept the serial equally well. The tie itself attributes nothing; each alternative is a complete decode under its own manufacturer. Send `manufacturer_id` to break the tie.",
            "example": "ambiguous_manufacturer"
          },
          "alternatives": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SerialNumberDecodeAlternative"
            },
            "minItems": 2,
            "description": "One complete decode per manufacturer whose format matched."
          }
        },
        "required": [
          "serial_number",
          "status",
          "alternatives"
        ]
      },
      "SerialNumberDecodeAlternative": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/SerialNumberDecodedAlternative"
          },
          {
            "$ref": "#/components/schemas/SerialNumberModelNotInCatalogAlternative"
          },
          {
            "$ref": "#/components/schemas/SerialNumberAmbiguousModelAlternative"
          }
        ],
        "discriminator": {
          "propertyName": "status",
          "mapping": {
            "decoded": "#/components/schemas/SerialNumberDecodedAlternative",
            "model_not_in_catalog": "#/components/schemas/SerialNumberModelNotInCatalogAlternative",
            "ambiguous_model": "#/components/schemas/SerialNumberAmbiguousModelAlternative"
          }
        }
      },
      "SerialNumberDecodedAlternative": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "decoded"
            ],
            "description": "The serial names exactly one asset the catalogue has.",
            "example": "decoded"
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The year encoded in the serial (the decoder does not say whether it is a model year or a build year), or `null` when the serial encodes none or its year code is unknown.",
            "example": 2021
          },
          "asset": {
            "$ref": "#/components/schemas/CatalogAssetReference"
          }
        },
        "required": [
          "status",
          "year",
          "asset"
        ]
      },
      "SerialNumberModelNotInCatalogAlternative": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "model_not_in_catalog"
            ],
            "description": "The serial names a model the catalogue does not have yet. The manufacturer is certain; use it, and read the model from `decoded_name_path`.",
            "example": "model_not_in_catalog"
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The year encoded in the serial (the decoder does not say whether it is a model year or a build year), or `null` when the serial encodes none or its year code is unknown.",
            "example": 2021
          },
          "asset": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "The manufacturer: the deepest catalogue asset the serial reaches."
              }
            ]
          },
          "decoded_name_path": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 2,
            "description": "The decoder's own name path for what the serial names, manufacturer first. Fixed when the decoding rules were written, so it is a label, not a catalogue name.",
            "example": [
              "Case IH",
              "Luxxum 100"
            ]
          }
        },
        "required": [
          "status",
          "year",
          "asset",
          "decoded_name_path"
        ]
      },
      "SerialNumberAmbiguousModelAlternative": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ambiguous_model"
            ],
            "description": "The serial names one manufacturer and one of several models (a type code shared across models).",
            "example": "ambiguous_model"
          },
          "year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The year encoded in the serial (the decoder does not say whether it is a model year or a build year), or `null` when the serial encodes none or its year code is unknown.",
            "example": 2021
          },
          "common_ancestor": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "What every candidate agrees on: the deepest catalogue asset all of them descend from (a family node, or the manufacturer). The safe attribution."
              }
            ]
          },
          "candidates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogAssetReference"
            },
            "description": "The assets the serial may name, each a descendant of `common_ancestor`. Narrow them with other evidence. Can be shorter than the decoder's own list when the catalogue lacks some of the models, even empty."
          }
        },
        "required": [
          "status",
          "year",
          "common_ancestor",
          "candidates"
        ]
      },
      "SerialNumberNoMatch": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "no_match"
            ],
            "description": "No format identified the serial, so nothing about it is known. Suspect the serial: check the case (decoding is case-sensitive), dashes and label prefixes.",
            "example": "no_match"
          }
        },
        "required": [
          "serial_number",
          "status"
        ]
      },
      "SerialNumberUnsupportedManufacturer": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "unsupported_manufacturer"
            ],
            "description": "The `manufacturer_id` sent names a manufacturer the decoder has no formats for, so the serial was never decoded and may be perfectly valid. Unlike `no_match`, no rules were tried.",
            "example": "unsupported_manufacturer"
          },
          "asset": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CatalogAssetReference"
              },
              {
                "description": "The manufacturer named by `manufacturer_id`."
              }
            ]
          }
        },
        "required": [
          "serial_number",
          "status",
          "asset"
        ]
      },
      "SerialNumberDecodeFailure": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "description": "The serial this result is for, exactly as sent.",
            "example": "KMTPC180JMKK00001"
          },
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "This item was not decoded, because of its own input or a catalogue gap. The other items in the request are unaffected.",
            "example": "error"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "manufacturer_not_found",
                  "manufacturer_not_in_catalog"
                ],
                "description": "`manufacturer_not_found`: the `manufacturer_id` sent is not an asset, or not a manufacturer (a root of the assets tree). `manufacturer_not_in_catalog`: the serial was recognized, but it belongs to a manufacturer the catalogue does not have yet.",
                "example": "manufacturer_not_found"
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation, safe to show to end users.",
                "example": "No manufacturer with id 36988."
              }
            },
            "required": [
              "code",
              "message"
            ],
            "description": "Why this item has no decode result."
          }
        },
        "required": [
          "serial_number",
          "status",
          "error"
        ]
      },
      "PhotoReading": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "The serial number as read off the photo, normalized (uppercase, no spaces), or `null` when the photo contains no readable serial. Show it to the end user for confirmation; if the read is slightly wrong, correct it and re-identify with the `serial_number` field. When `null` and no other input was provided, `data` is empty; there is no error status for an unreadable serial.",
            "example": "KMTPC180JMKK00001"
          },
          "serial_number_confidence": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "Confidence of the photo read between 0 and 1, or `null` when no serial was read. It scores the reading of the characters, not the matches: decoding the read serial into assets is deterministic and unscored. Low values mean the serial may have been misread; confirm `serial_number` with the end user before trusting the matches.",
            "example": 0.97
          },
          "image_width": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Width in pixels of the photo as processed, after EXIF orientation was applied (the orientation a browser displays by default). Annotation coordinates are fractions of this frame; compare against your rendered image to detect an orientation mismatch.",
            "example": 3024
          },
          "image_height": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Height in pixels of the photo as processed, after EXIF orientation was applied.",
            "example": 4032
          },
          "text_annotations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PhotoTextAnnotation"
            },
            "description": "Every text region recognized in the photo, in reading order (top to bottom), with normalized bounding rectangles for drawing an overlay on the uploaded image: multiply `x`/`width` by the rendered width and `y`/`height` by the rendered height. Coordinates are relative to the EXIF-oriented image (origin top-left). Regions with `is_serial_number: true` are the ones the serial was read from."
          }
        },
        "required": [
          "serial_number",
          "serial_number_confidence",
          "image_width",
          "image_height",
          "text_annotations"
        ],
        "description": "What was read off the uploaded photo. Present exactly when the request carried a `photo`; absent for JSON requests."
      },
      "PhotoTextAnnotation": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "Recognized text verbatim as it appears in the photo, including spacing and label prefixes as printed. For serial regions this may differ from `photo.serial_number`, which is normalized for matching.",
            "example": "KMTPC180JM KK00001"
          },
          "is_serial_number": {
            "type": "boolean",
            "description": "`true` when this region is (part of) the serial number that was read. A serial stamped across several lines yields several regions with this flag; concatenated in array order they form the read that `photo.serial_number` was normalized from.",
            "example": true
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Recognition confidence for this region's text, between 0 and 1.",
            "example": 0.97
          },
          "x": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Left edge of the bounding rectangle as a fraction of image width (0 = left edge, 1 = right edge).",
            "example": 0.38
          },
          "y": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Top edge of the bounding rectangle as a fraction of image height (0 = top edge, 1 = bottom edge).",
            "example": 0.535
          },
          "width": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Width of the bounding rectangle as a fraction of image width.",
            "example": 0.35
          },
          "height": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Height of the bounding rectangle as a fraction of image height.",
            "example": 0.032
          }
        },
        "required": [
          "text",
          "is_serial_number",
          "confidence",
          "x",
          "y",
          "width",
          "height"
        ]
      },
      "IdentifyRequest": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500,
            "description": "Free-text description of the asset as it appears in your source system: dealer feeds, spreadsheets, invoices. Abbreviations, mixed languages, and noise are expected.",
            "example": "komatsu pc210lc-8 escavatore cingolato 2021 usato"
          },
          "serial_number": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64,
            "description": "Serial number as stamped on the asset's identification plate, 1–64 characters. Sent to the decoder unchanged: keep the stamped case and dashes (they are part of some formats), but strip label prefixes such as `SERIAL NO`.",
            "example": "KMTPC180JMKK00001"
          },
          "manufacturer_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Optional asset id of a manufacturer (a root node of the assets tree) to decode the serial against. A hint, not evidence: it narrows serial decoding and it is not billed. A manufacturer prefix in the serial itself overrides it. An id that is not a manufacturer is reported as a `manufacturer_not_found` error in `serial_decode`, not as a request failure.",
            "example": 1777
          }
        },
        "additionalProperties": false
      },
      "IdentifyPhotoRequest": {
        "type": "object",
        "properties": {
          "photo": {
            "type": "string",
            "format": "binary",
            "description": "Photo showing the asset's serial number: an identification plate, a stamped frame, a sticker. JPEG, PNG, WebP, or HEIC/HEIF, at most 10 MB."
          },
          "description": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500,
            "description": "Free-text description of the asset as it appears in your source system: dealer feeds, spreadsheets, invoices. Abbreviations, mixed languages, and noise are expected.",
            "example": "komatsu pc210lc-8 escavatore cingolato 2021 usato"
          },
          "serial_number": {
            "type": "string",
            "minLength": 3,
            "maxLength": 64,
            "description": "Serial number as stamped on the asset's identification plate, 1–64 characters. Sent to the decoder unchanged: keep the stamped case and dashes (they are part of some formats), but strip label prefixes such as `SERIAL NO`.",
            "example": "KMTPC180JMKK00001"
          },
          "manufacturer_id": {
            "type": "integer",
            "description": "Optional asset id of a manufacturer (a root node of the assets tree) to decode the serial against. A hint, not evidence: it narrows serial decoding and it is not billed. A manufacturer prefix in the serial itself overrides it. An id that is not a manufacturer is reported as a `manufacturer_not_found` error in `serial_decode`, not as a request failure. Sent as a text form field.",
            "example": 1777
          }
        },
        "required": [
          "photo"
        ],
        "additionalProperties": false
      },
      "DecodeSerialNumbersResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SerialNumberDecodeResult"
            },
            "description": "One result per requested serial, in request order, each echoing its `serial_number`. Branch on `status`; the other fields depend on it."
          }
        },
        "required": [
          "results"
        ]
      },
      "SerialDecodingUnavailableError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "serial_decoding_unavailable"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "DecodeSerialNumbersRequest": {
        "type": "object",
        "properties": {
          "serial_numbers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SerialNumberDecodeItem"
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "Serials to decode, 1–10 per call. Each item is decoded and billed on its own."
          }
        },
        "required": [
          "serial_numbers"
        ],
        "additionalProperties": false
      },
      "SerialNumberDecodeItem": {
        "type": "object",
        "properties": {
          "serial_number": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Serial number as stamped on the asset's identification plate, 1–64 characters. Sent to the decoder unchanged: keep the stamped case and dashes (they are part of some formats), but strip label prefixes such as `SERIAL NO`.",
            "example": "KMTPC180JMKK00001"
          },
          "manufacturer_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Optional asset id of a manufacturer (a root node of the assets tree) to decode against. A hint, not evidence: it restricts decoding to that manufacturer's formats and is not billed. A manufacturer prefix in the serial itself (a WMI) overrides it. An id that is not a manufacturer yields a `manufacturer_not_found` error for that item only.",
            "example": 1777
          }
        },
        "required": [
          "serial_number"
        ],
        "additionalProperties": false
      },
      "OperationCo2": {
        "type": "object",
        "properties": {
          "asset_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the asset the figures belong to.",
            "example": 1234
          },
          "co2_per_unit": {
            "type": [
              "number",
              "null"
            ],
            "description": "Operational CO2e emitted per usage unit (see `co2_unit`).",
            "example": 32.7
          },
          "co2_unit": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unit of `co2_per_unit`.",
            "example": "kg/h"
          },
          "co2_per_year": {
            "type": [
              "number",
              "null"
            ],
            "description": "Operational CO2e per year in kilograms, computed from `co2_per_unit` and the typical yearly usage.",
            "example": 58860
          },
          "yearly_usage_amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "Typical yearly usage assumed for `co2_per_year`.",
            "example": 1800
          },
          "yearly_usage_unit": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unit of `yearly_usage_amount`.",
            "example": "h/year"
          },
          "fuel_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fuel or energy carrier of the asset. Multi-fuel assets list every carrier slash-joined (e.g. `petrol/electric`).",
            "example": "diesel"
          },
          "fuel_consumptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FuelConsumption"
            },
            "description": "Consumption figures backing the CO2 numbers. Empty when not available."
          }
        },
        "required": [
          "asset_id",
          "co2_per_unit",
          "co2_unit",
          "co2_per_year",
          "yearly_usage_amount",
          "yearly_usage_unit",
          "fuel_type",
          "fuel_consumptions"
        ]
      },
      "FuelConsumption": {
        "type": "object",
        "properties": {
          "fuel_type": {
            "type": "string",
            "description": "Fuel or energy carrier this consumption entry refers to.",
            "example": "diesel"
          },
          "consumption": {
            "type": "number",
            "description": "Consumption amount per `consumption_unit`.",
            "example": 12.4
          },
          "consumption_unit": {
            "type": "string",
            "description": "Unit of the consumption amount.",
            "example": "l/h"
          },
          "source": {
            "type": "string",
            "description": "Provenance of this figure. `asset` is a value recorded for this exact asset and `asset_representative_descendant` comes from a representative descendant asset, while `taxonomy_*` values are calculated from the asset's classification (for example `taxonomy_specification_power_adjusted` is the taxonomy figure scaled to this asset's engine power). New values may be added over time.",
            "example": "asset"
          }
        },
        "required": [
          "fuel_type",
          "consumption",
          "consumption_unit",
          "source"
        ]
      },
      "ProductionCo2": {
        "type": "object",
        "properties": {
          "asset_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the asset the figures belong to.",
            "example": 1234
          },
          "material_kgco2e": {
            "type": [
              "number",
              "null"
            ],
            "description": "Embodied emissions of the asset's materials, in kg CO2e.",
            "example": 78100
          },
          "energy_kgco2e": {
            "type": [
              "number",
              "null"
            ],
            "description": "Manufacturing-energy emissions, in kg CO2e.",
            "example": 18300
          },
          "total_kgco2e": {
            "type": [
              "number",
              "null"
            ],
            "description": "Total production emissions (materials + manufacturing energy), in kg CO2e.",
            "example": 96400
          },
          "material_composition": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialCompositionEntry"
            },
            "description": "Material breakdown of the asset, ordered by share, largest first. Empty when not available."
          }
        },
        "required": [
          "asset_id",
          "material_kgco2e",
          "energy_kgco2e",
          "total_kgco2e",
          "material_composition"
        ]
      },
      "MaterialCompositionEntry": {
        "type": "object",
        "properties": {
          "material": {
            "type": "string",
            "description": "Material name.",
            "example": "steel"
          },
          "mass_kg": {
            "type": [
              "number",
              "null"
            ],
            "description": "Mass of this material in the asset, in kilograms. `null` when only the share is known.",
            "example": 15330
          },
          "share": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Fraction of the asset's total mass, between 0 and 1.",
            "example": 0.7
          }
        },
        "required": [
          "material",
          "mass_kg",
          "share"
        ]
      },
      "EndOfLifeCo2": {
        "type": "object",
        "properties": {
          "asset_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the asset the figures belong to.",
            "example": 1234
          },
          "recycling_emissions_kgco2e": {
            "type": [
              "number",
              "null"
            ],
            "description": "Emissions caused by the recycling process itself, in kg CO2e.",
            "example": 4100
          },
          "recycling_credit_kgco2e": {
            "type": [
              "number",
              "null"
            ],
            "description": "Avoided-burden credit for recovered materials, in kg CO2e. Positive number; subtract it from the emissions.",
            "example": 12600
          },
          "net_kgco2e": {
            "type": [
              "number",
              "null"
            ],
            "description": "`recycling_emissions_kgco2e` minus `recycling_credit_kgco2e`. Negative when recycling is a net benefit.",
            "example": -8500
          },
          "material_composition": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MaterialCompositionEntry"
            },
            "description": "Material breakdown of the asset, ordered by share, largest first. Empty when not available."
          }
        },
        "required": [
          "asset_id",
          "recycling_emissions_kgco2e",
          "recycling_credit_kgco2e",
          "net_kgco2e",
          "material_composition"
        ]
      },
      "TechnicalSpecification": {
        "type": "object",
        "properties": {
          "asset_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the asset the specification belongs to.",
            "example": 1234
          },
          "weight_kg": {
            "type": [
              "number",
              "null"
            ],
            "description": "Operating weight in kilograms.",
            "example": 21900
          },
          "engine_power_kw": {
            "type": [
              "number",
              "null"
            ],
            "description": "Rated engine power in kilowatts.",
            "example": 122
          },
          "fuel_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fuel or energy carrier of the asset. Multi-fuel assets list every carrier slash-joined (e.g. `petrol/electric`).",
            "example": "diesel"
          }
        },
        "required": [
          "asset_id",
          "weight_kg",
          "engine_power_kw",
          "fuel_type"
        ]
      },
      "TechnicalSpecificationAttributeListResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TechnicalSpecificationAttribute"
            },
            "description": "Page of the asset's technical specification attributes."
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Total count before paging. Only present when `include_total=true` was passed.",
            "example": 12
          }
        },
        "required": [
          "data"
        ]
      },
      "TechnicalSpecificationAttribute": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Canonical attribute key, stable across assets and shared with the taxonomy vocabulary: join asset values to taxonomy definitions on this field. For numeric attributes the normalized unit is part of the key (e.g. `dig_depth_mm`). The key set is data, not schema: keys may be added, merged, or retired as document coverage grows.",
            "example": "dig_depth_mm"
          },
          "label": {
            "type": "string",
            "description": "Canonical human-readable label of the attribute, identical for every asset.",
            "example": "Maximum digging depth"
          },
          "value": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              }
            ],
            "description": "Normalized value: a number for numeric attributes (in the attribute's canonical unit) or a string for textual ones, matching the `data_type` the taxonomy vocabulary declares for this `key`.",
            "example": 6720
          },
          "unit": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unit of the normalized value. `null` for unitless (e.g. textual) attributes. Fixed per `key`: every asset reports this attribute in the same unit.",
            "example": "mm"
          }
        },
        "required": [
          "key",
          "label",
          "value",
          "unit"
        ]
      },
      "TaxonomyTechnicalSpecificationAttributeListResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaxonomyTechnicalSpecificationAttribute"
            },
            "description": "Page of the taxonomy's attribute vocabulary, ordered by descending `coverage`."
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "description": "Total count before paging. Only present when `include_total=true` was passed.",
            "example": 8
          }
        },
        "required": [
          "data"
        ]
      },
      "TaxonomyTechnicalSpecificationAttribute": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Canonical attribute key, stable across assets and shared with the taxonomy vocabulary: join asset values to taxonomy definitions on this field. For numeric attributes the normalized unit is part of the key (e.g. `dig_depth_mm`). The key set is data, not schema: keys may be added, merged, or retired as document coverage grows.",
            "example": "dig_depth_mm"
          },
          "label": {
            "type": "string",
            "description": "Canonical human-readable label of the attribute, identical for every asset.",
            "example": "Maximum digging depth"
          },
          "data_type": {
            "type": "string",
            "enum": [
              "number",
              "string"
            ],
            "description": "Type of the `value` asset entries carry for this key. New types may be added over time.",
            "example": "number"
          },
          "unit": {
            "type": [
              "string",
              "null"
            ],
            "description": "Unit of the normalized value. `null` for unitless (e.g. textual) attributes. Fixed per `key`: every asset reports this attribute in the same unit.",
            "example": "mm"
          },
          "coverage": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "How common this attribute is in the taxonomy's documentation: the fraction of documented assets under this node (those with processed manufacturer documentation) that have a value for it, between 0 and 1. Use it to pick comparison columns (e.g. keep keys with coverage >= 0.8). Note that an asset without processed documentation returns no value even for high-coverage keys.",
            "example": 0.91
          }
        },
        "required": [
          "key",
          "label",
          "data_type",
          "unit",
          "coverage"
        ]
      },
      "GeoContent": {
        "type": "object",
        "properties": {
          "asset_id": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Id of the asset the content belongs to.",
            "example": 1234
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "One-sentence plain-text summary of the asset.",
            "example": "The Caterpillar 320 GC is a 21.9-tonne crawler excavator built for lower-cost-per-hour production work."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Long-form plain-text description of the asset. Paragraphs separated by blank lines.",
            "example": "The Caterpillar 320 GC pairs a 122 kW engine with a reduced-power hydraulic system…"
          },
          "highlights": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Short bullet-style facts about the asset. Empty when not available."
          },
          "applications": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Typical applications and industries the asset is used in. Empty when not available."
          }
        },
        "required": [
          "asset_id",
          "summary",
          "description",
          "highlights",
          "applications"
        ]
      }
    }
  },
  "paths": {
    "/api/v1/vault-files": {
      "get": {
        "summary": "List asset vault files",
        "description": "Returns up to 50 asset vault files belonging to the authenticated user.",
        "tags": [
          "AssetVault"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Up to 50 vault files",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VaultFileListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/suggest": {
      "post": {
        "summary": "Suggest catalog entries",
        "description": "Ranked autocomplete over the catalog (assets + taxonomies). Scope the results with `parent_id` (asset subtree), `taxonomy_id` (classification subtree), and `direct_only` (first level of the scope only; see each parameter for how they combine). Unlike the browse endpoints, scope ids are not existence-checked: an unknown id yields an empty result rather than a 404, keeping autocomplete responsive.",
        "tags": [
          "Catalog"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "query": "komatsu pc210",
                "entity_type": "asset",
                "parent_id": 1234
              },
              "schema": {
                "$ref": "#/components/schemas/CatalogSuggestRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked suggestions (0 to 5 entries)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSuggestResponse"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "415": {
            "description": "Request body Content-Type matches none of the declared media types",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsupportedMediaTypeError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/taxonomies": {
      "get": {
        "summary": "List taxonomies",
        "description": "Paginated taxonomy list. If no `parent_id` is provided, returns root taxonomies. Pass `parent_id` to fetch direct children of a node.",
        "tags": [
          "Catalog"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of items to skip. Defaults to 0.",
              "example": 0
            },
            "required": false,
            "description": "Number of items to skip. Defaults to 0.",
            "in": "query",
            "name": "offset"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Number of items per page. Defaults to 50, capped at 100.",
              "example": 50
            },
            "required": false,
            "description": "Number of items per page. Defaults to 50, capped at 100.",
            "in": "query",
            "name": "limit"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false",
              "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
              "example": "false"
            },
            "required": false,
            "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
            "in": "query",
            "name": "include_total"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "List direct children of this taxonomy.",
              "example": 3
            },
            "required": false,
            "description": "List direct children of this taxonomy.",
            "in": "query",
            "name": "parent_id"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of taxonomy nodes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogTaxonomyListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Referenced `parent_id` does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/taxonomies/{taxonomy_id}": {
      "get": {
        "summary": "Get a taxonomy",
        "description": "Fetch a single taxonomy node by id.",
        "tags": [
          "Catalog"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Taxonomy id.",
              "example": 12
            },
            "required": true,
            "description": "Taxonomy id.",
            "in": "path",
            "name": "taxonomy_id"
          }
        ],
        "responses": {
          "200": {
            "description": "The taxonomy node",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogTaxonomyNode"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Taxonomy not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets": {
      "get": {
        "summary": "List assets",
        "description": "Paginated asset list ordered by name. With no filters, returns top-level assets (manufacturers). `parent_id` and `taxonomy_id` each narrow the results (see each parameter); combining them returns the direct children of `parent_id` that also match the taxonomy filter.",
        "tags": [
          "Catalog"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of items to skip. Defaults to 0.",
              "example": 0
            },
            "required": false,
            "description": "Number of items to skip. Defaults to 0.",
            "in": "query",
            "name": "offset"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Number of items per page. Defaults to 50, capped at 100.",
              "example": 50
            },
            "required": false,
            "description": "Number of items per page. Defaults to 50, capped at 100.",
            "in": "query",
            "name": "limit"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false",
              "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
              "example": "false"
            },
            "required": false,
            "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
            "in": "query",
            "name": "include_total"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "List direct children of this asset.",
              "example": 100
            },
            "required": false,
            "description": "List direct children of this asset.",
            "in": "query",
            "name": "parent_id"
          },
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Include assets whose subtree contains anything classified under this taxonomy or its descendants.",
              "example": 12
            },
            "required": false,
            "description": "Include assets whose subtree contains anything classified under this taxonomy or its descendants.",
            "in": "query",
            "name": "taxonomy_id"
          }
        ],
        "responses": {
          "200": {
            "description": "Page of assets (0 to `limit`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogAssetListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Referenced `parent_id` or `taxonomy_id` does not exist",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets/{asset_id}": {
      "get": {
        "summary": "Get an asset",
        "description": "Fetch a single asset node by id.",
        "tags": [
          "Catalog"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Asset id.",
              "example": 1234
            },
            "required": true,
            "description": "Asset id.",
            "in": "path",
            "name": "asset_id"
          }
        ],
        "responses": {
          "200": {
            "description": "The asset node",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogAssetNode"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/identify": {
      "post": {
        "summary": "Identify an asset",
        "x-preview": true,
        "description": "Identify a catalog asset from any combination of evidence: a free-text `description` (dealer feeds, spreadsheets, invoices), a `serial_number` as stamped on the identification plate, and/or a `photo` showing the serial. Send text-only evidence as `application/json`; include a photo by sending `multipart/form-data` with the image in the `photo` field and the other inputs as text form fields. At least one input is required. The response is one fused, ranked match list: matches supported by several inputs rank first, and deterministic serial decodes rank above probabilistic description matches. When a serial was available (the `serial_number` field, or one read off the photo), `serial_decode` reports how it was decoded, in the same shape as `POST /catalog/serial-numbers/decode`: the status says how far the serial could be trusted, and the serial-based matches derive from it. When a photo was sent, `photo` echoes what was read off it: the normalized serial, its read confidence, and every recognized text region with a normalized bounding rectangle for drawing overlays. A photo with no readable serial is not an error: `photo.serial_number` is `null` and any other provided inputs still match. Pass `manufacturer_id` as a hint when the same serial format is used by several manufacturers; an id that is not a manufacturer is reported in `serial_decode` as an `error`, and the other inputs still match. Each provided input is billed additively at its own credit weight. Note: this is a preview endpoint that currently returns illustrative mock data; upload a zero-byte file to preview the unreadable-photo shape.",
        "tags": [
          "Identification"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": "komatsu pc210lc-8 escavatore cingolato 2021 usato"
              },
              "schema": {
                "$ref": "#/components/schemas/IdentifyRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/IdentifyPhotoRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ranked matches across all provided inputs (0 to 5 entries), plus the serial decode when a serial was available and the photo reading when a photo was sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdentifyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "415": {
            "description": "Request body Content-Type matches none of the declared media types",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsupportedMediaTypeError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/serial-numbers/decode": {
      "post": {
        "summary": "Decode serial numbers",
        "description": "Decode up to 10 serial numbers in one call and match each to the AssetBase catalogue. Decoding is deterministic: the same serial always resolves the same way, and there is no confidence score. Instead, every result carries a `status` saying how far the serial could be trusted: `decoded` (one asset), `model_not_in_catalog` (a model the catalogue lacks; the manufacturer is certain), `ambiguous_model` (one manufacturer, several candidate models), `unknown_model` (manufacturer and year; the model code is not in the rules), `checksum_failed` (manufacturer only; distrust the serial), `unrecognized_format` (manufacturer only, from the prefix), `ambiguous_manufacturer` (several manufacturers' formats fit), `no_match` (nothing identified), `unsupported_manufacturer` (the `manufacturer_id` sent has no formats), or `error` (a fault on that item). Results come back in request order, one per serial, each echoing its `serial_number`. A fault on one item never fails the request: it is reported as that item's `error` and the other items decode normally. Send `manufacturer_id` as a hint when the same format is used by several manufacturers; a manufacturer prefix in the serial overrides it. Serials are decoded exactly as sent (case-sensitive). Each serial is billed at its own credit weight, whatever its outcome.",
        "tags": [
          "Identification"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "serial_numbers": [
                  {
                    "serial_number": "KMTPC180JMKK00001"
                  },
                  {
                    "serial_number": "158300001",
                    "manufacturer_id": 1777
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/DecodeSerialNumbersRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result per requested serial, in request order",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DecodeSerialNumbersResponse"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "415": {
            "description": "Request body Content-Type matches none of the declared media types",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsupportedMediaTypeError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          },
          "503": {
            "description": "The decoder is unavailable, timed out or rate-limited; retry shortly",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SerialDecodingUnavailableError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets/{asset_id}/co2/operation": {
      "get": {
        "summary": "Get operational CO2",
        "description": "Operational (use-phase) CO2 figures for the asset: emissions per usage unit and per year, with the fuel/energy consumption figures backing them. Fields are `null` where no data exists. Figures are served from a periodically refreshed snapshot, so recent catalog edits may take up to a day to appear.",
        "tags": [
          "Asset Data"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Asset id.",
              "example": 1234
            },
            "required": true,
            "description": "Asset id.",
            "in": "path",
            "name": "asset_id"
          }
        ],
        "responses": {
          "200": {
            "description": "Operational CO2 figures",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OperationCo2"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets/{asset_id}/co2/production": {
      "get": {
        "summary": "Get production CO2",
        "description": "Production (cradle-to-gate) CO2 figures for the asset: embodied material emissions, manufacturing-energy emissions, and their total, in kg CO2e, plus the material composition the material emissions are based on. Material masses are derived from the asset's operating weight; when only shares are known, `mass_kg` is `null`. Fields are `null` where no data exists. Figures are served from a periodically refreshed snapshot, so recent catalog edits may take up to a day to appear.",
        "tags": [
          "Asset Data"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Asset id.",
              "example": 1234
            },
            "required": true,
            "description": "Asset id.",
            "in": "path",
            "name": "asset_id"
          }
        ],
        "responses": {
          "200": {
            "description": "Production CO2 figures",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductionCo2"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets/{asset_id}/co2/end-of-life": {
      "get": {
        "summary": "Get end-of-life CO2",
        "x-preview": true,
        "description": "End-of-life CO2 figures for the asset: recycling-process emissions, the avoided-burden credit for recovered materials, and the net result, in kg CO2e, plus the material composition the recovery figures are based on. Fields are `null` where no data exists. Note: this is a preview endpoint that currently returns illustrative mock data.",
        "tags": [
          "Asset Data"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Asset id.",
              "example": 1234
            },
            "required": true,
            "description": "Asset id.",
            "in": "path",
            "name": "asset_id"
          }
        ],
        "responses": {
          "200": {
            "description": "End-of-life CO2 figures",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndOfLifeCo2"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets/{asset_id}/technical-specification": {
      "get": {
        "summary": "Get the technical specification",
        "description": "The asset's technical specification: operating weight, engine power, fuel type, and further typed spec fields as coverage grows. Fields are `null` where no data exists. Where a representative variant is designated for the asset, figures come from that variant's specification. Served directly from the catalog, so recent edits appear immediately.",
        "tags": [
          "Asset Data"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Asset id.",
              "example": 1234
            },
            "required": true,
            "description": "Asset id.",
            "in": "path",
            "name": "asset_id"
          }
        ],
        "responses": {
          "200": {
            "description": "The technical specification",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TechnicalSpecification"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets/{asset_id}/technical-specification-attributes": {
      "get": {
        "summary": "List technical specification attributes",
        "description": "Every technical specification attribute extracted for the asset from manufacturer documentation: attributes shared across the asset's taxonomy and asset-specific ones alike. Values are normalized: each `key` is canonical with one fixed unit, so the same key is directly comparable across assets. To learn which keys are common for a taxonomy (e.g. to pick comparison columns), call `GET /catalog/taxonomies/{taxonomy_id}/technical-specification-attributes` and join on `key`. The key set is data, not schema: keys are added, merged, and retired as document coverage grows; ordering is stable per asset but unspecified, so join on `key`, not position.",
        "tags": [
          "Asset Data"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Asset id.",
              "example": 1234
            },
            "required": true,
            "description": "Asset id.",
            "in": "path",
            "name": "asset_id"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of items to skip. Defaults to 0.",
              "example": 0
            },
            "required": false,
            "description": "Number of items to skip. Defaults to 0.",
            "in": "query",
            "name": "offset"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Number of items per page. Defaults to 50, capped at 100.",
              "example": 50
            },
            "required": false,
            "description": "Number of items per page. Defaults to 50, capped at 100.",
            "in": "query",
            "name": "limit"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false",
              "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
              "example": "false"
            },
            "required": false,
            "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
            "in": "query",
            "name": "include_total"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the asset's technical specification attributes",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TechnicalSpecificationAttributeListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/taxonomies/{taxonomy_id}/technical-specification-attributes": {
      "get": {
        "summary": "List taxonomy technical specification attributes",
        "description": "The technical-specification vocabulary of a taxonomy: every attribute key that appears on assets under this node, with its canonical label, value type, unit, and `coverage`. Coverage is how common the attribute is among the node's documented assets (those with processed manufacturer documentation). Use it to discover which attributes are common (and therefore comparable) across a taxonomy before fetching per-asset values from `GET /catalog/assets/{asset_id}/technical-specification-attributes`; the two join on `key`. Entries are ordered by descending `coverage`. Coverage is computed on read from the current catalog, so recent changes appear immediately.",
        "tags": [
          "Asset Data"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Taxonomy id.",
              "example": 12
            },
            "required": true,
            "description": "Taxonomy id.",
            "in": "path",
            "name": "taxonomy_id"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of items to skip. Defaults to 0.",
              "example": 0
            },
            "required": false,
            "description": "Number of items to skip. Defaults to 0.",
            "in": "query",
            "name": "offset"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50,
              "description": "Number of items per page. Defaults to 50, capped at 100.",
              "example": 50
            },
            "required": false,
            "description": "Number of items per page. Defaults to 50, capped at 100.",
            "in": "query",
            "name": "limit"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false",
              "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
              "example": "false"
            },
            "required": false,
            "description": "Opt-in to a `total` field in the response. Defaults to `false`. Case-insensitive.",
            "in": "query",
            "name": "include_total"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of the taxonomy's attribute vocabulary, most common first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaxonomyTechnicalSpecificationAttributeListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Taxonomy not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/catalog/assets/{asset_id}/geo-content": {
      "get": {
        "summary": "Get GEO content",
        "x-preview": true,
        "description": "Editorial content about the asset written for generative-engine and search visibility: a summary, a long-form description, highlights, and typical applications. All fields are typed; text is plain text. Note: this is a preview endpoint that currently returns illustrative mock data.",
        "tags": [
          "Asset Data"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 2147483647,
              "description": "Asset id.",
              "example": 1234
            },
            "required": true,
            "description": "Asset id.",
            "in": "path",
            "name": "asset_id"
          }
        ],
        "responses": {
          "200": {
            "description": "GEO content for the asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GeoContent"
                }
              }
            }
          },
          "400": {
            "description": "Request validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/ApiKeyMissingError"
                    },
                    {
                      "$ref": "#/components/schemas/ApiKeyInvalidError"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "API key lacks the required permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionDeniedError"
                }
              }
            }
          },
          "404": {
            "description": "Asset not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotFoundError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; slow down and retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitExceededError"
                }
              }
            }
          },
          "500": {
            "description": "Unhandled server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InternalErrorError"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}