{
  "openapi": "3.1.0",
  "info": {
    "title": "nitrovin HTTP API",
    "version": "v1",
    "summary": "Offline VIN decoding that answers like NHTSA's vPIC.",
    "description": "`nitrovin serve`. Every route answers with the artifact and the\nvocabularies (`--vocab`) the server loaded at startup; the server is\nready when it is alive. Wire types live in `decode.schema.json`; the\nrules this document cannot express (layouts, item order, the clock,\ntranslation and listing, versioning) are in `README.md` beside it.\n\nWithin `/v1` changes are additive only: new fields, routes and optional\nparameters. Element names and codes change with data months; that is data,\nnot a breaking change. Pin `element_id` for anything long-lived.\n",
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    }
  },
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "security": [],
  "servers": [
    {
      "url": "http://127.0.0.1:8080",
      "description": "`nitrovin serve` default address"
    }
  ],
  "paths": {
    "/v1/decode/{vin}": {
      "get": {
        "operationId": "decodeOne",
        "summary": "Decode one VIN.",
        "description": "Any VIN of 1-17 characters after trimming decodes, partial (`*`) and\nmistyped ones included; what is wrong with it is in `error_codes`.\nA VIN with vPIC error codes is always 200.\n",
        "parameters": [
          {
            "name": "vin",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "1HGCM82633A004352"
          },
          {
            "$ref": "#/components/parameters/model_year"
          },
          {
            "$ref": "#/components/parameters/full"
          },
          {
            "$ref": "#/components/parameters/shape"
          },
          {
            "$ref": "#/components/parameters/now"
          }
        ],
        "responses": {
          "200": {
            "description": "The decode.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Decode"
                },
                "examples": {
                  "nitrovin": {
                    "$ref": "#/components/examples/decodeNitrovin"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/v1/decode": {
      "post": {
        "operationId": "decodeBatch",
        "summary": "Decode up to 1,000 VINs.",
        "description": "A JSON array of decode requests in, a JSON array of decodes out, same\nlength and order. The wall clock is read once for the whole request;\nan item's `now` overrides it for that item. Any invalid item refuses\nthe whole request with 400, its `pointer` naming the item\n(`/3/vin`).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchRequest"
              },
              "example": [
                {
                  "vin": "1HGCM82633A004352"
                },
                {
                  "vin": "1HGCM82633A004352",
                  "model_year": 2010,
                  "shape": "nitrovin"
                },
                {
                  "vin": "5YJ3E1EA2JF000316",
                  "full": true,
                  "now": "2026-09-01"
                }
              ]
            }
          }
        },
        "responses": {
          "200": {
            "description": "One decode per request item, in order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/v1/translate": {
      "get": {
        "operationId": "translateOne",
        "summary": "Translate one VIN, vocabulary entry or spine entry.",
        "description": "One translate request as query parameters, each the request's path\nwith dots: `from` is `from.vin`; or `from.vocabulary` with one\n`from.entry.<field>` per field of the vocabulary\n(`from.entry.make=Dodge&from.entry.model=Ram%201500`); or one of\n`from.make`, `from.model`, `from.ext`, or `from.spine` with\n`from.id`. `to` is repeated, once per target vocabulary. Every\nparameter but `to` is given at most once, and an unknown one is\nrefused. The answer, and any refusal with its `pointer`, is the one\nthe same request gets as a `POST /v1/translate` item. Nothing to\ntranslate is never an error: an unknown or unmapped entry answers\nwith its `status` and empty lists.\n",
        "parameters": [
          {
            "name": "from.vin",
            "in": "query",
            "description": "A VIN to translate from, as a decode's `vin`.",
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "1D7RV1GT5AS123456"
          },
          {
            "name": "from.vocabulary",
            "in": "query",
            "description": "A loaded vocabulary to translate an entry of, its fields given as\n`from.entry.<field>`, exactly the vocabulary's fields.\n",
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "yard"
          },
          {
            "name": "from.make",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SpineRefFields/properties/make"
            }
          },
          {
            "name": "from.model",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SpineRefFields/properties/model"
            }
          },
          {
            "name": "from.ext",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SpineRefFields/properties/ext"
            }
          },
          {
            "name": "from.spine",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SpineRefFields/properties/spine"
            }
          },
          {
            "name": "from.id",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SpineRefFields/properties/id"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "A target vocabulary, on the same spine as `from`; repeated for several. None answers the spine alone.",
            "schema": {
              "$ref": "#/components/schemas/TranslateRequestFields/properties/to"
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "model_year",
            "in": "query",
            "description": "The model year, the VIN's decode hint, and the year when no VIN decodes one; any 32-bit integer.",
            "schema": {
              "type": "integer",
              "minimum": -2147483648,
              "maximum": 2147483647
            }
          },
          {
            "name": "vin",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TranslateRequestFields/properties/vin"
            }
          },
          {
            "$ref": "#/components/parameters/now"
          }
        ],
        "responses": {
          "200": {
            "description": "The translation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Translation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      },
      "post": {
        "operationId": "translateBatch",
        "summary": "Translate up to 1,000 requests.",
        "description": "A JSON array of translate requests in, a JSON array of translations\nout, same length and order. The wall clock is read once for the\nwhole request; an item's `now` overrides it for that item. Any\nrefused item refuses the whole request with 400, its `pointer`\nnaming the item (`/3/to/0`).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "minItems": 1,
                "maxItems": 1000,
                "items": {
                  "$ref": "#/components/schemas/TranslateRequest"
                }
              },
              "example": [
                {
                  "from": "1D7RV1GT5AS123456",
                  "to": [
                    "yard"
                  ]
                },
                {
                  "from": {
                    "vocabulary": "yard",
                    "entry": {
                      "make": "Dodge",
                      "model": "Ram 1500"
                    }
                  },
                  "model_year": 2010
                },
                {
                  "from": {
                    "make": 476
                  },
                  "to": [
                    "yard"
                  ]
                }
              ]
            }
          }
        },
        "responses": {
          "200": {
            "description": "One translation per request item, in order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Translation"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/v1/vocabularies": {
      "get": {
        "operationId": "listCatalog",
        "summary": "What is loaded, by name.",
        "description": "The catalog: vPIC, then every loaded vocabulary, extension set and\ndeclared spine, in load order. It is neither filtered nor paged, so\nany query parameter is refused.\n",
        "responses": {
          "200": {
            "description": "The catalog, its entries `CatalogEntry` values.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/v1/vocabularies/{name}/entries": {
      "get": {
        "operationId": "listEntries",
        "summary": "Page through vPIC or one loaded file.",
        "description": "One page of what `name` lists: vPIC's makes (`vpic`), or one make's\nmodels (`vpic` with `where.make`); a vocabulary's entries with their\nmappings; a declared spine's entries; an extension set's extensions.\n`where.<field>` matches a field exactly after the fixed\nnormalization, once per field (`where.make=Dodge`). Every parameter\nis given at most once, and an unknown one is refused.\n",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "`vpic`, or a name from the catalog.",
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "example": "yard"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Entries a page.",
            "schema": {
              "$ref": "#/components/schemas/ListRequestFields/properties/limit"
            }
          },
          {
            "name": "after",
            "in": "query",
            "description": "The previous page's `next`.",
            "schema": {
              "$ref": "#/components/schemas/ListRequestFields/properties/after"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/v1/info": {
      "get": {
        "operationId": "info",
        "summary": "Decoder and data identity, and the data month's element table.",
        "description": "The same object as `nitrovin info`.",
        "responses": {
          "200": {
            "description": "Identity of what answers every decode.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Info"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "operationId": "openapi",
        "summary": "This document, as JSON, with decode.schema.json bundled in.",
        "responses": {
          "200": {
            "description": "The OpenAPI document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "operationId": "health",
        "summary": "Liveness; unversioned.",
        "responses": {
          "200": {
            "description": "Serving. The artifact loads before the listener opens.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "const": "ok"
                    }
                  },
                  "additionalProperties": false
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcp",
        "summary": "MCP over Streamable HTTP; the messages are opaque here.",
        "description": "`nitrovin mcp`'s server, its tools `decode_vin`, `translate` and\n`list` as `mcp-tools.json` defines them, over MCP's Streamable HTTP transport.\nStateless: every POST is a session of its own, with no\n`Mcp-Session-Id`, and every response is `application/json`, never an\nevent stream, so `GET` and `DELETE` are 405. A POST carries one\nJSON-RPC message, with `Content-Type: application/json` and an\n`Accept` naming both `application/json` and `text/event-stream`, as\nthe transport requires. The messages are MCP's and the results\n`README.md`'s MCP section, not described here. The transport's own\nrefusals (a wrong `Content-Type` or `Accept`, an unsupported\n`MCP-Protocol-Version`, a body over 1 MiB) are the MCP SDK's, as\ntext.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "description": "One JSON-RPC message."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC response to a request.",
            "content": {
              "application/json": {
                "schema": {
                  "description": "One JSON-RPC message."
                }
              }
            }
          },
          "202": {
            "description": "Accepted, no body; the message was a notification or a response."
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "model_year": {
        "name": "model_year",
        "in": "query",
        "description": "The model-year hint (vPIC's `modelyear`); any 32-bit integer.",
        "schema": {
          "type": "integer",
          "minimum": -2147483648,
          "maximum": 2147483647
        }
      },
      "full": {
        "name": "full",
        "in": "query",
        "description": "Add `items` to the decode.",
        "schema": {
          "type": "string",
          "enum": [
            "true",
            "false"
          ],
          "default": "false"
        }
      },
      "shape": {
        "name": "shape",
        "in": "query",
        "description": "Layout of `attributes`.",
        "schema": {
          "type": "string",
          "enum": [
            "vpic",
            "nitrovin"
          ],
          "default": "vpic"
        }
      },
      "now": {
        "name": "now",
        "in": "query",
        "description": "Frozen clock: an RFC 3339 instant, or a date meaning 00:00:00Z that\nday. Absent: the wall clock, read once per request.\n",
        "schema": {
          "$ref": "#/components/schemas/DecodeRequestFields/properties/now"
        },
        "example": "2026-09-01T00:00:00Z"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request cannot be answered as asked: malformed JSON, a field of\nthe wrong type, an unknown field or query parameter, an empty or\nover-17-character VIN, a bad `model_year`, `full`, `shape` or `now`.\nFor translate and list, also what the loaded files refuse: a name\nnothing loaded has, entry or `where` fields the vocabulary lacks, a\ntarget on another spine, an `after` that is not a cursor.\n",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://github.com/joshuasprow/nitrovin/blob/main/docs/api/README.md#invalid-request",
              "title": "Invalid request",
              "status": 400,
              "detail": "vin is 18 characters after trimming spaces; the limit is 17",
              "pointer": "/3/vin"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such route.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "The route exists; the method does not.",
        "headers": {
          "Allow": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "TooLarge": {
        "description": "More than 1,000 items, or a body over 1 MiB.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Internal": {
        "description": "A fault in nitrovin, never a property of the VIN.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "examples": {
      "decodeNitrovin": {
        "summary": "`?shape=nitrovin`, 2026_10, attributes abridged",
        "value": {
          "vin": "1HGCM82633A004352",
          "wmi": "1HG",
          "model_year": 2003,
          "error_codes": [
            0
          ],
          "check_digit_valid": true,
          "corrected_vin": null,
          "model_year_hint": null,
          "now": "2026-11-07T21:00:00Z",
          "data_month": "2026_10",
          "decoder_version": "v1.0.0",
          "shape": "nitrovin",
          "attributes": {
            "Make": {
              "element_id": 26,
              "value": "HONDA",
              "attribute_id": 474
            },
            "Model": {
              "element_id": 28,
              "value": "Accord",
              "attribute_id": 1861
            },
            "Model Year": {
              "element_id": 29,
              "value": "2003"
            },
            "Displacement (L)": {
              "element_id": 13,
              "value": "2.998832712"
            },
            "Other Restraint System Info": {
              "element_id": 121,
              "values": [
                "Seat Belt (Rr center position)"
              ]
            },
            "Error Code": {
              "element_id": 143,
              "value": "0"
            },
            "Suggested VIN": {
              "element_id": 142,
              "value": ""
            }
          }
        }
      }
    },
    "schemas": {
      "Decode": {
        "type": "object",
        "required": [
          "vin",
          "wmi",
          "model_year",
          "error_codes",
          "check_digit_valid",
          "corrected_vin",
          "model_year_hint",
          "now",
          "data_month",
          "decoder_version",
          "shape",
          "attributes"
        ],
        "properties": {
          "vin": {
            "type": "string",
            "minLength": 1,
            "maxLength": 17,
            "description": "The VIN as decoded: the request's vin trimmed, uppercased, with every non-ASCII character replaced by '&'."
          },
          "wmi": {
            "type": "string",
            "maxLength": 6,
            "description": "Positions 1-3, plus 12-14 when position 3 is '9' and the VIN has at least 14 characters."
          },
          "model_year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The model year the winning pass decoded (element 29); null when none was resolved."
          },
          "error_codes": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "minItems": 1,
            "uniqueItems": true,
            "description": "vPIC's error codes (element 143), ascending. [0] is a clean decode."
          },
          "check_digit_valid": {
            "type": "boolean",
            "description": "True when the VIN has at least 17 characters and position 9 equals the computed check digit. A vPIC VinException suppresses error code 1 but leaves this false."
          },
          "corrected_vin": {
            "type": [
              "string",
              "null"
            ],
            "description": "vPIC's suggested VIN (element 142, '!' at positions in error); null when that element is empty."
          },
          "model_year_hint": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The request's model_year, echoed; null when none was given."
          },
          "now": {
            "type": "string",
            "format": "date-time",
            "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
            "description": "The clock this decode used, in UTC whole seconds: the request's now, or the wall clock read once per request."
          },
          "data_month": {
            "type": "string",
            "pattern": "^[0-9]{4}_(0[1-9]|1[0-2])$",
            "description": "The data month of the loaded artifact."
          },
          "decoder_version": {
            "type": "string",
            "minLength": 1,
            "description": "The nitrovin module version that decoded, e.g. 'v1.2.0'; '(devel)' for an untagged build."
          },
          "shape": {
            "enum": [
              "vpic",
              "nitrovin"
            ],
            "description": "The layout of attributes, as requested (default 'vpic')."
          },
          "attributes": {
            "description": "The decoded vehicle, laid out per shape."
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Item"
            },
            "description": "Present only when the request set full: every item the decoder emitted, in item order (docs/api/README.md)."
          }
        },
        "additionalProperties": false,
        "if": {
          "properties": {
            "shape": {
              "const": "vpic"
            }
          }
        },
        "then": {
          "properties": {
            "attributes": {
              "$ref": "#/components/schemas/VpicAttributes"
            }
          }
        },
        "else": {
          "properties": {
            "attributes": {
              "$ref": "#/components/schemas/NitrovinAttributes"
            }
          }
        }
      },
      "VpicAttributes": {
        "type": "object",
        "description": "vPIC's DecodeVinValues layout. One key per public element of the data month, named by its element code, always present ('' when undecoded), plus MakeID, ManufacturerId and ModelID. A multi-valued element's distinct values are joined with ', ' in byte order.",
        "propertyNames": {
          "pattern": "^[A-Za-z][A-Za-z0-9_]*$"
        },
        "properties": {
          "MakeID": {
            "type": "string"
          },
          "ManufacturerId": {
            "type": "string"
          },
          "ModelID": {
            "type": "string"
          }
        },
        "required": [
          "MakeID",
          "ManufacturerId",
          "ModelID"
        ],
        "additionalProperties": {
          "type": "string"
        }
      },
      "NitrovinAttributes": {
        "type": "object",
        "description": "Keyed by variable name; only decoded elements are present. A multi-valued element always carries values, every other element value.",
        "propertyNames": {
          "minLength": 1
        },
        "additionalProperties": {
          "oneOf": [
            {
              "$ref": "#/components/schemas/SingleAttribute"
            },
            {
              "$ref": "#/components/schemas/MultiAttribute"
            }
          ]
        }
      },
      "SingleAttribute": {
        "type": "object",
        "required": [
          "element_id",
          "value"
        ],
        "properties": {
          "element_id": {
            "type": "integer"
          },
          "value": {
            "type": "string",
            "description": "The oracle's exact text; numbers are never retyped."
          },
          "attribute_id": {
            "type": "integer",
            "description": "vPIC's attribute id; present only on lookup elements whose id is an integer (never on Error Code, 143)."
          }
        },
        "additionalProperties": false
      },
      "MultiAttribute": {
        "type": "object",
        "required": [
          "element_id",
          "values"
        ],
        "properties": {
          "element_id": {
            "type": "integer"
          },
          "values": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "description": "Every attribute of the element, duplicates kept, in byte order."
          }
        },
        "additionalProperties": false
      },
      "Item": {
        "type": "object",
        "description": "One row of spvindecode's output for this VIN, all 15 columns, SQL NULL as null.",
        "required": [
          "group_name",
          "variable",
          "value",
          "pattern_id",
          "vin_schema_id",
          "keys",
          "element_id",
          "attribute_id",
          "created_on",
          "wmi_id",
          "code",
          "data_type",
          "decode",
          "source",
          "to_be_qced"
        ],
        "properties": {
          "group_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "groupname"
          },
          "variable": {
            "type": "string",
            "description": "variable"
          },
          "value": {
            "type": [
              "string",
              "null"
            ],
            "description": "value"
          },
          "pattern_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "itempatternid"
          },
          "vin_schema_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "itemvinschemaid"
          },
          "keys": {
            "type": [
              "string",
              "null"
            ],
            "description": "itemkeys"
          },
          "element_id": {
            "type": "integer",
            "description": "itemelementid"
          },
          "attribute_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "itemattributeid as text: a lookup id, a code list ('1,11,400'), or the raw attribute."
          },
          "created_on": {
            "type": [
              "string",
              "null"
            ],
            "description": "itemcreatedon as Postgres renders a timestamp, e.g. '2015-04-21 11:37:26.607'."
          },
          "wmi_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "itemwmiid"
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "code (the element code)"
          },
          "data_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "datatype"
          },
          "decode": {
            "type": [
              "string",
              "null"
            ],
            "description": "decode"
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "description": "itemsource, e.g. 'Pattern', 'Conversion 2: 183 * 16.387064 '"
          },
          "to_be_qced": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "itemtobeqced"
          }
        },
        "additionalProperties": false
      },
      "DecodeRequestFields": {
        "type": "object",
        "required": [
          "vin"
        ],
        "properties": {
          "vin": {
            "type": "string",
            "minLength": 1,
            "description": "1-17 characters after trimming spaces: vPIC's spvindecode holds the trimmed VIN in a varchar(17) and raises on a longer one. Any such string decodes; partial ('*') and mistyped VINs return error codes, not a refusal."
          },
          "model_year": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": -2147483648,
            "maximum": 2147483647,
            "description": "The model-year hint (vPIC's modelyear)."
          },
          "full": {
            "type": "boolean",
            "default": false,
            "description": "Add items to the decode."
          },
          "shape": {
            "enum": [
              "vpic",
              "nitrovin"
            ],
            "default": "vpic"
          },
          "now": {
            "type": "string",
            "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}(T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?([Zz]|[+-][0-9]{2}:[0-9]{2}))?$",
            "description": "Frozen clock: an RFC 3339 instant, or a date meaning 00:00:00Z that day. Fractional seconds are truncated. Absent: the wall clock, read once per request."
          }
        }
      },
      "DecodeRequest": {
        "description": "One decode request: an element of the POST /v1/decode array, a GET's path and query, a C ABI request.",
        "$ref": "#/components/schemas/DecodeRequestFields",
        "unevaluatedProperties": false
      },
      "BatchRequest": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/DecodeRequest"
        },
        "minItems": 1,
        "maxItems": 1000
      },
      "BatchResponse": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Decode"
        },
        "description": "One decode per request item, same order, same length."
      },
      "StdioRequest": {
        "type": "object",
        "description": "One line of `nitrovin stdio` input, and the request_json of nitrovin_decode: op's request plus an optional id.",
        "properties": {
          "id": {
            "description": "Any JSON value; echoed on the reply."
          },
          "op": {
            "enum": [
              "decode",
              "translate",
              "list"
            ],
            "default": "decode",
            "description": "The request beside it: a DecodeRequest, a TranslateRequest or a ListRequest. The reply is keyed decode, translation or list."
          }
        },
        "if": {
          "required": [
            "op"
          ],
          "properties": {
            "op": {
              "const": "translate"
            }
          }
        },
        "then": {
          "$ref": "#/components/schemas/TranslateRequestFields"
        },
        "else": {
          "if": {
            "required": [
              "op"
            ],
            "properties": {
              "op": {
                "const": "list"
              }
            }
          },
          "then": {
            "$ref": "#/components/schemas/ListRequestFields"
          },
          "else": {
            "$ref": "#/components/schemas/DecodeRequestFields"
          }
        },
        "unevaluatedProperties": false
      },
      "StdioReply": {
        "description": "One line of `nitrovin stdio` output, and the return of nitrovin_decode: the request's id when it had one, then its op's answer or an error.",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "decode"
            ],
            "properties": {
              "id": {},
              "decode": {
                "$ref": "#/components/schemas/Decode"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "translation"
            ],
            "properties": {
              "id": {},
              "translation": {
                "$ref": "#/components/schemas/Translation"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "list"
            ],
            "properties": {
              "id": {},
              "list": {
                "$ref": "#/components/schemas/List"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "error"
            ],
            "properties": {
              "id": {},
              "error": {
                "$ref": "#/components/schemas/Problem"
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. The type URI is one of the problem types in docs/api/README.md.",
        "required": [
          "type",
          "title",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "pointer": {
            "type": "string",
            "description": "JSON Pointer into the request to the offending value, e.g. '/3/vin' for item 3 of a batch; on GET, '/<query parameter>' or '/vin'."
          }
        }
      },
      "Info": {
        "type": "object",
        "description": "GET /v1/info, `nitrovin info`, nitrovin_info.",
        "required": [
          "decoder_version",
          "data_month",
          "artifact_hash",
          "data_source",
          "elements",
          "vocabularies"
        ],
        "properties": {
          "decoder_version": {
            "type": "string",
            "minLength": 1
          },
          "data_month": {
            "type": "string",
            "pattern": "^[0-9]{4}_(0[1-9]|1[0-2])$"
          },
          "artifact_hash": {
            "type": "string",
            "pattern": "^[a-z0-9]+:[0-9a-f]+$",
            "description": "<algorithm>:<hex> of the loaded artifact's bytes; the algorithm is the artifact format's."
          },
          "data_source": {
            "type": "string",
            "minLength": 1,
            "description": "'embedded', or the absolute path of the artifact loaded with --data, NITROVIN_DATA or nitrovin_open."
          },
          "elements": {
            "type": "array",
            "description": "Every public element of the data month, by element_id.",
            "items": {
              "type": "object",
              "required": [
                "element_id",
                "variable",
                "code",
                "group_name",
                "data_type",
                "multi_valued"
              ],
              "properties": {
                "element_id": {
                  "type": "integer"
                },
                "variable": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                },
                "group_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "data_type": {
                  "type": "string"
                },
                "multi_valued": {
                  "type": "boolean"
                }
              },
              "additionalProperties": false
            }
          },
          "vocabularies": {
            "type": "array",
            "description": "The vocabularies, extension sets and declared spines loaded beside the data (--vocab, NITROVIN_VOCAB), in load order; empty when none are. Decisions: ADR 0011, ADR 0012.",
            "items": {
              "type": "object",
              "required": [
                "name",
                "kind",
                "version",
                "blake3",
                "path",
                "drift"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1
                },
                "kind": {
                  "enum": [
                    "vocabulary",
                    "extension-set",
                    "spine"
                  ]
                },
                "version": {
                  "type": "string",
                  "minLength": 1
                },
                "blake3": {
                  "type": "string",
                  "pattern": "^[0-9a-f]{64}$",
                  "description": "The hex blake3 hash of the file's bytes."
                },
                "path": {
                  "type": "string",
                  "minLength": 1,
                  "description": "The absolute path the file was loaded from."
                },
                "drift": {
                  "type": "array",
                  "description": "The file's drift against the data month, by kind, only the kinds it has, in the order make, model, qualifier, extension; empty when it agrees with the month. Drift is served, never refused.",
                  "items": {
                    "type": "object",
                    "required": [
                      "kind",
                      "count",
                      "examples"
                    ],
                    "properties": {
                      "kind": {
                        "enum": [
                          "make",
                          "model",
                          "qualifier",
                          "extension"
                        ],
                        "description": "make, model: a vPIC id the month lacks. qualifier: an element the month lacks, a value of the wrong type for it, or a value its decode never produces. extension: an extension named like a vPIC make, or a vPIC model under the same make."
                      },
                      "count": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "examples": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 3,
                        "description": "The first findings of the kind, in line order.",
                        "items": {
                          "type": "object",
                          "required": [
                            "line",
                            "detail"
                          ],
                          "properties": {
                            "line": {
                              "type": "integer",
                              "minimum": 1
                            },
                            "detail": {
                              "type": "string"
                            }
                          },
                          "additionalProperties": false
                        }
                      }
                    },
                    "additionalProperties": false
                  }
                }
              },
              "additionalProperties": false
            }
          }
        },
        "additionalProperties": false
      },
      "TranslateRequestFields": {
        "type": "object",
        "description": "A translate request's fields (ADR 0012): from a VIN, a vocabulary entry or a spine entry, through exactly one spine, to the target vocabularies. Contract prose: docs/api/README.md, Translation.",
        "required": [
          "from"
        ],
        "properties": {
          "from": {
            "oneOf": [
              {
                "type": "string",
                "minLength": 1,
                "description": "A VIN, 1-17 characters after trimming spaces, as a decode request's vin."
              },
              {
                "$ref": "#/components/schemas/VocabularyEntryRef"
              },
              {
                "$ref": "#/components/schemas/SpineRef"
              }
            ]
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "description": "Target vocabulary names, each a loaded vocabulary on the same spine as from. Absent or empty: the spine alone."
          },
          "model_year": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": -2147483648,
            "maximum": 2147483647,
            "description": "The model year: the VIN's decode hint, and the year when no VIN decodes one. A VIN's decoded year wins over it."
          },
          "vin": {
            "type": "string",
            "minLength": 1,
            "description": "A context VIN when from is an entry: its decode checks qualifiers and gives the model year. Refused beside a VIN from."
          },
          "now": {
            "type": "string",
            "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}(T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?([Zz]|[+-][0-9]{2}:[0-9]{2}))?$",
            "description": "Frozen clock for the VIN's decode, as a decode request's now."
          }
        },
        "if": {
          "required": [
            "from"
          ],
          "properties": {
            "from": {
              "type": "string"
            }
          }
        },
        "then": {
          "not": {
            "required": [
              "vin"
            ]
          }
        }
      },
      "TranslateRequest": {
        "description": "One translate request, the same object on every surface (ADR 0012); on stdio, a line with op translate.",
        "$ref": "#/components/schemas/TranslateRequestFields",
        "unevaluatedProperties": false
      },
      "VocabularyEntryRef": {
        "type": "object",
        "description": "An entry of a loaded vocabulary, by its fields: exactly the vocabulary's fields, matched after trimming, collapsing whitespace and Unicode lower-casing.",
        "required": [
          "vocabulary",
          "entry"
        ],
        "properties": {
          "vocabulary": {
            "type": "string",
            "minLength": 1
          },
          "entry": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      },
      "SpineRefFields": {
        "type": "object",
        "description": "One spine entry as a mapping target names it: exactly one of a vPIC make id, a vPIC model id, an extension, or a declared spine's name with one of its ids.",
        "properties": {
          "make": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2147483647,
            "description": "A vPIC make id."
          },
          "model": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2147483647,
            "description": "A vPIC model id."
          },
          "ext": {
            "type": "string",
            "pattern": "^ext:[a-z0-9][a-z0-9._-]*/[^/\\s]+$",
            "description": "An extension, ext:<set>/<id>."
          },
          "spine": {
            "type": "string",
            "minLength": 1,
            "description": "A declared spine's name, beside id."
          },
          "id": {
            "type": "string",
            "minLength": 1,
            "description": "An entry id of the declared spine."
          }
        },
        "oneOf": [
          {
            "required": [
              "make"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "model"
                  ]
                },
                {
                  "required": [
                    "ext"
                  ]
                },
                {
                  "required": [
                    "spine"
                  ]
                },
                {
                  "required": [
                    "id"
                  ]
                }
              ]
            }
          },
          {
            "required": [
              "model"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "make"
                  ]
                },
                {
                  "required": [
                    "ext"
                  ]
                },
                {
                  "required": [
                    "spine"
                  ]
                },
                {
                  "required": [
                    "id"
                  ]
                }
              ]
            }
          },
          {
            "required": [
              "ext"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "make"
                  ]
                },
                {
                  "required": [
                    "model"
                  ]
                },
                {
                  "required": [
                    "spine"
                  ]
                },
                {
                  "required": [
                    "id"
                  ]
                }
              ]
            }
          },
          {
            "required": [
              "spine",
              "id"
            ],
            "not": {
              "anyOf": [
                {
                  "required": [
                    "make"
                  ]
                },
                {
                  "required": [
                    "model"
                  ]
                },
                {
                  "required": [
                    "ext"
                  ]
                }
              ]
            }
          }
        ]
      },
      "SpineRef": {
        "$ref": "#/components/schemas/SpineRefFields",
        "unevaluatedProperties": false
      },
      "Translation": {
        "type": "object",
        "description": "The answer to a TranslateRequest: the route from from through the spine to each target vocabulary. Nothing to translate is never a refusal: an unknown or unmapped entry, or a target with no entries, answers with a status and empty lists.",
        "required": [
          "from",
          "spine",
          "to",
          "vocabularies",
          "now",
          "data_month",
          "decoder_version"
        ],
        "properties": {
          "from": {
            "$ref": "#/components/schemas/TranslationFrom"
          },
          "vehicle": {
            "$ref": "#/components/schemas/Vehicle"
          },
          "spine": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SpineEntry"
            },
            "description": "Every spine entry reached, once each, in route order: each mapping's spine entry, then the make a vPIC or extension model sits under; for a VIN, its model then its make."
          },
          "to": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Translated"
            },
            "description": "One per target vocabulary, in request order, each named once."
          },
          "vocabularies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileVersion"
            },
            "description": "The files the translation used, in load order."
          },
          "now": {
            "type": "string",
            "format": "date-time",
            "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
            "description": "The clock the VIN's decode used (resolved when there is no VIN), in UTC whole seconds."
          },
          "data_month": {
            "type": "string",
            "pattern": "^[0-9]{4}_(0[1-9]|1[0-2])$"
          },
          "decoder_version": {
            "type": "string",
            "minLength": 1
          }
        },
        "additionalProperties": false
      },
      "TranslationFrom": {
        "type": "object",
        "description": "The request's from with its status: the VIN as decoded; the entry as its vocabulary writes it (as the request did when unknown) with the entry's note; or the spine entry.",
        "required": [
          "status"
        ],
        "properties": {
          "vin": {
            "type": "string"
          },
          "vocabulary": {
            "type": "string"
          },
          "entry": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "make": {
            "type": "integer"
          },
          "model": {
            "type": "integer"
          },
          "ext": {
            "type": "string"
          },
          "spine": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "status": {
            "enum": [
              "mapped",
              "unmapped",
              "unknown"
            ],
            "description": "mapped: the entry has mappings, or the VIN or spine entry reaches the spine. unmapped: the vocabulary lists the entry without mappings. unknown: the vocabulary or spine has no such entry, or the VIN decodes to no vPIC make or model."
          },
          "note": {
            "type": "string",
            "description": "The entry's note, passed through."
          }
        },
        "additionalProperties": false
      },
      "Vehicle": {
        "type": "object",
        "description": "What a translation reads from a VIN's decode, from or context: never the whole decode.",
        "required": [
          "vin",
          "make",
          "model",
          "model_year"
        ],
        "properties": {
          "vin": {
            "type": "string",
            "description": "The VIN as decoded."
          },
          "make": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The decoded vPIC make id (element 26's attribute id)."
          },
          "model": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The decoded vPIC model id (element 28's attribute id)."
          },
          "model_year": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The decoded model year (element 29)."
          }
        },
        "additionalProperties": false
      },
      "SpineEntry": {
        "description": "One spine entry in an answer, named from the data month or its file.",
        "$ref": "#/components/schemas/SpineRefFields",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "Absent for a declared-spine entry its file leaves unnamed, and with drift."
          },
          "drift": {
            "const": true,
            "description": "A vPIC id the data month lacks, named by a mapping and served anyway (ADR 0011)."
          },
          "under": {
            "$ref": "#/components/schemas/SpineRef",
            "description": "The make a vPIC or extension model sits under."
          },
          "via": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Via"
            },
            "description": "In a translation from a vocabulary entry: the entry's mappings that reached this spine entry. A make reached because a model sits under it has the model's mapping."
          },
          "unconfirmed": {
            "$ref": "#/components/schemas/Unconfirmed"
          }
        },
        "unevaluatedProperties": false
      },
      "Via": {
        "description": "One mapping that held on a route: the spine entry it names, its years, origin and note as its file has them, and what could not be checked on the route through it.",
        "$ref": "#/components/schemas/SpineRefFields",
        "properties": {
          "first": {
            "type": "integer"
          },
          "last": {
            "type": "integer"
          },
          "origin": {
            "enum": [
              "derived",
              "curated",
              "seed"
            ]
          },
          "note": {
            "type": "string"
          },
          "unconfirmed": {
            "$ref": "#/components/schemas/Unconfirmed"
          }
        },
        "unevaluatedProperties": false
      },
      "Unconfirmed": {
        "type": "array",
        "minItems": 1,
        "uniqueItems": true,
        "items": {
          "oneOf": [
            {
              "type": "integer",
              "minimum": 1
            },
            {
              "const": "model_year"
            }
          ]
        },
        "description": "What applied but could not be checked: qualifier element ids, ascending, with no decode or that the decode left empty, then \"model_year\" when a year range applied and no year was known. Absent when everything was checked."
      },
      "Translated": {
        "type": "object",
        "required": [
          "vocabulary",
          "entries"
        ],
        "properties": {
          "vocabulary": {
            "type": "string",
            "minLength": 1
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TranslatedEntry"
            },
            "description": "Each entry reached once, in list order (by normalized fields)."
          }
        },
        "additionalProperties": false
      },
      "TranslatedEntry": {
        "type": "object",
        "required": [
          "entry",
          "via"
        ],
        "properties": {
          "entry": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "note": {
            "type": "string",
            "description": "The entry's own note."
          },
          "via": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Via"
            },
            "description": "Every mapping of the entry that held, each naming the spine entry that led to it, in spine order."
          },
          "unconfirmed": {
            "$ref": "#/components/schemas/Unconfirmed",
            "description": "Absent when one via checked everything, so a client can keep only fully confirmed entries."
          }
        },
        "additionalProperties": false
      },
      "FileVersion": {
        "type": "object",
        "required": [
          "name",
          "version"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "version": {
            "type": "string",
            "minLength": 1
          }
        },
        "additionalProperties": false
      },
      "ListRequestFields": {
        "type": "object",
        "description": "A list request's fields (ADR 0012). Without of: the catalog of what is loaded. With of: one page of vPIC ('vpic') or a loaded file. Contract prose: docs/api/README.md, Listing.",
        "properties": {
          "of": {
            "type": "string",
            "minLength": 1
          },
          "where": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "integer"
              ]
            },
            "description": "Fields to match after the fixed normalization: a vocabulary's fields, or for vpic only make, a vPIC make id, to list its models."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000,
            "default": 100
          },
          "after": {
            "type": "string",
            "minLength": 1,
            "description": "The previous page's next cursor."
          }
        }
      },
      "ListRequest": {
        "description": "One list request, the same object on every surface (ADR 0012); on stdio, a line with op list.",
        "$ref": "#/components/schemas/ListRequestFields",
        "unevaluatedProperties": false
      },
      "List": {
        "type": "object",
        "description": "The answer to a ListRequest: the catalog, or one page of what of names.",
        "required": [
          "entries",
          "vocabularies",
          "data_month",
          "decoder_version"
        ],
        "properties": {
          "of": {
            "type": "string"
          },
          "entries": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/CatalogEntry"
                },
                {
                  "$ref": "#/components/schemas/VocabularyEntry"
                },
                {
                  "$ref": "#/components/schemas/SpineEntry"
                }
              ]
            },
            "description": "CatalogEntry for the catalog; VocabularyEntry for a vocabulary; SpineEntry for vpic, a declared spine or an extension set."
          },
          "next": {
            "type": "string",
            "minLength": 1,
            "description": "The after of the next page; absent on the last."
          },
          "vocabularies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileVersion"
            },
            "description": "The files listed: every loaded file for the catalog, the one of names, none for vpic."
          },
          "data_month": {
            "type": "string",
            "pattern": "^[0-9]{4}_(0[1-9]|1[0-2])$"
          },
          "decoder_version": {
            "type": "string",
            "minLength": 1
          }
        },
        "additionalProperties": false
      },
      "CatalogEntry": {
        "type": "object",
        "required": [
          "name",
          "kind",
          "version",
          "subject"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "kind": {
            "enum": [
              "vocabulary",
              "extension-set",
              "spine"
            ],
            "description": "vpic is a spine."
          },
          "version": {
            "type": "string",
            "minLength": 1,
            "description": "A file's version; vpic's is the data month."
          },
          "subject": {
            "enum": [
              "vehicle",
              "part"
            ]
          },
          "fields": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            },
            "description": "A vocabulary's fields, in order."
          },
          "spine": {
            "type": "string",
            "minLength": 1,
            "description": "The spine a vocabulary or extension set routes through: vpic, or a declared spine's name."
          }
        },
        "additionalProperties": false
      },
      "VocabularyEntry": {
        "type": "object",
        "description": "One vocabulary entry as its file has it.",
        "required": [
          "entry",
          "mappings"
        ],
        "properties": {
          "entry": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "mappings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VocabularyMapping"
            },
            "description": "Empty for an unmapped entry."
          },
          "note": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "VocabularyMapping": {
        "type": "object",
        "description": "One mapping as its file has it: vocabulary.schema.json's Mapping.",
        "properties": {
          "make": {
            "type": "integer",
            "minimum": 1
          },
          "model": {
            "type": "integer",
            "minimum": 1
          },
          "ext": {
            "type": "string",
            "pattern": "^ext:[a-z0-9][a-z0-9._-]*/[^/\\s]+$"
          },
          "id": {
            "type": "string",
            "minLength": 1
          },
          "first": {
            "type": "integer"
          },
          "last": {
            "type": "integer"
          },
          "qualifiers": {
            "type": "object",
            "minProperties": 1,
            "propertyNames": {
              "pattern": "^[1-9][0-9]*$"
            },
            "additionalProperties": {
              "type": "array",
              "minItems": 1,
              "items": {
                "type": [
                  "integer",
                  "string"
                ]
              }
            }
          },
          "origin": {
            "enum": [
              "derived",
              "curated",
              "seed"
            ]
          },
          "note": {
            "type": "string"
          }
        },
        "oneOf": [
          {
            "required": [
              "make"
            ]
          },
          {
            "required": [
              "model"
            ]
          },
          {
            "required": [
              "ext"
            ]
          },
          {
            "required": [
              "id"
            ]
          }
        ],
        "additionalProperties": false
      }
    }
  }
}
