{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/gunspec-io/json-schema/gunspec-io-firearm-search-result-schema.json",
  "title": "FirearmSearchResult",
  "description": "One search hit. **The field names are snake_case here**, unlike `/v1/firearms` and the rest of the catalog: these are the search view's own columns and renaming them would break every consumer already reading them. `manufacturer_name` and `category_name` are joined in so a result list renders without a second call.",
  "x-generated": "2026-10-30",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/gunspec-io-openapi.json#/components/schemas/FirearmSearchResult",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "URL-safe slug identifying the firearm. Stable: it is what a mirror keys on."
    },
    "name": {
      "type": "string",
      "description": "Display name, as the maker writes it."
    },
    "manufacturer_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Slug of the manufacturer. `manufacturerId` on the camelCase endpoints."
    },
    "category_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "Slug of the category. `categoryId` on the camelCase endpoints."
    },
    "manufacturer_name": {
      "type": [
        "string",
        "null"
      ],
      "description": "Display name of the manufacturer, joined in so a result row needs no second call."
    },
    "category_name": {
      "type": [
        "string",
        "null"
      ],
      "description": "Display name of the category."
    },
    "category_slug": {
      "type": [
        "string",
        "null"
      ],
      "description": "Slug of the category again, under the name the search view uses. Same value as `category_id`."
    },
    "year_introduced": {
      "type": [
        "integer",
        "null"
      ],
      "description": "Year the model was introduced."
    },
    "status": {
      "anyOf": [
        {
          "$ref": "#/$defs/FirearmStatus"
        },
        {
          "type": "null"
        }
      ],
      "description": "Production status, one of `in_production`, `discontinued`, `out_of_production`, `in_service`, `limited_production`, `prototype`. Discontinued records are kept deliberately: a rifle built in 2004 wears parts nobody sells today."
    },
    "country_of_origin": {
      "type": [
        "string",
        "null"
      ],
      "description": "ISO 3166-1 alpha-2 code of the country of origin."
    },
    "action_type": {
      "type": [
        "string",
        "null"
      ],
      "description": "Operating action as stored, underscored, e.g. `short_recoil`, `bolt_action`, `gas_operated`. The full vocabulary is `/v1/firearms/action-types`; a hyphenated value matches nothing."
    },
    "weight_empty_g": {
      "type": [
        "number",
        "null"
      ],
      "description": "Unloaded weight in grams, without magazine."
    },
    "barrel_length_mm": {
      "type": [
        "number",
        "null"
      ],
      "description": "Barrel length in millimetres, measured from the breech face."
    },
    "svg_line_art_url": {
      "type": [
        "string",
        "null"
      ],
      "description": "Line-art silhouette, or null where none has been drawn."
    },
    "model_3d_url": {
      "type": [
        "string",
        "null"
      ],
      "description": "GLB model, or null where none is on file."
    },
    "favorite_count": {
      "type": "integer",
      "description": "How many accounts have favorited this record."
    },
    "rank": {
      "type": [
        "number",
        "null"
      ],
      "description": "FTS5 relevance score the page is ordered by. Lower is a better match (SQLite bm25 scores are negative), and it is only comparable within one response. Absent or null when the query fell back to a plain substring match, which has no ranking."
    },
    "images": {
      "type": "array",
      "description": "Every image this firearm has, silhouette first. Empty when none are on file.",
      "items": {
        "$ref": "#/$defs/InlineMediaItem"
      }
    },
    "matched_name": {
      "type": [
        "string",
        "null"
      ],
      "description": "The firearm's alternate name the query matched, when its own name did not: a special edition is filed as an alternate name on the model it is an edition of, so searching for the edition returns the model with the edition's name here. Null when the record's own name matched, or the match came from the maker, a caliber or the description."
    },
    "matched_name_kind": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "alternate",
        "edition",
        null
      ],
      "description": "`edition` when `matched_name` is an edition of this model (a limited, anniversary, engraved or finish edition), `alternate` when it is another name for it. Null when `matched_name` is null."
    }
  },
  "required": [
    "id",
    "name"
  ],
  "$defs": {
    "FirearmStatus": {
      "type": "string",
      "enum": [
        "in_production",
        "discontinued",
        "out_of_production",
        "in_service",
        "limited_production",
        "prototype"
      ],
      "description": "Production status of a firearm. Closed: these are every value the catalog holds."
    },
    "InlineMediaItem": {
      "type": "object",
      "description": "One image attached to a firearm, inlined on list-shaped responses so a client can render a gallery without a second request. `url` is absolute and directly fetchable. For the full envelope, including credits, licence, dimensions and data-URI encoding, use `/v1/firearms/{id}/media`.",
      "properties": {
        "id": {
          "type": "integer",
          "description": "Autoincrementing image id."
        },
        "url": {
          "type": "string",
          "description": "Absolute, directly fetchable URL."
        },
        "kind": {
          "$ref": "#/$defs/MediaKind"
        },
        "alt": {
          "type": [
            "string",
            "null"
          ],
          "description": "Alt text, where one has been written. Null otherwise, so do not render an empty string."
        },
        "width": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Pixel width, where it is known."
        },
        "height": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Pixel height, where it is known."
        }
      },
      "required": [
        "id",
        "url",
        "kind"
      ]
    },
    "MediaKind": {
      "type": "string",
      "enum": [
        "silhouette",
        "render",
        "photo",
        "schematic",
        "model"
      ],
      "description": "What kind of asset a media row is."
    }
  }
}