Registry entry

Nigeria FRSC National Vehicle Identification System (NVIS) — Vehicle Registration

The Federal Road Safety Corps' (FRSC) National Vehicle Identification System (NVIS), a live, unauthenticated ASP.NET web form for registering a motor vehicle in Nigeria — https://nvis.frsc.gov.ng/VehicleManagement/RegisterVehicle. FRSC operates NVIS as Nigeria's national vehicle-registration platform (distinct from the separate 'Verify Number Plate' and 'Vehicle Revalidation' flows linked from the same navigation bar). The page renders directly at HTTP 200 with no login redirect, CAPTCHA, or WAF gate — a genuine citizen-facing intake form, not a staff-only or authenticated portal. The form uses ASP.NET MVC's unobtrusive client-side validation (data-val/data-val-required/data-val-length attributes rendered directly on each input/select), which this schema's requiredness follows field-by-field rather than assuming a uniform convention across every field: several fields a prior scouting pass expected to be required (Owner Identification Type, Title, First Name, Last Name, Company Name, State Of Plate Number Allocation) carry no data-val-required attribute on the live page and are modeled required:false accordingly. The submit button is labeled 'Preview' (not 'Submit'/'Register'), implying a further confirmation/payment step beyond this single-page data-entry form; this schema scopes to the data collected on this page only. Two fields are disclosed-cap controlled vocabularies rather than enumerated in full: vehicleMakeId (the live 'Vehicle Make' dropdown carries 1,569

Registry entry

ng/frsc/vehicle-registration

Jurisdiction
Nigeria · national
Version
1.0.0
Verification
draft

Authoritative source FRSC National Vehicle Identification System (NVIS) — "Vehicle Registration" online form

Machine access

Schema document
registry/ng/frsc/vehicle-registration/1.0.0/schema.jsonapplication/schema+json
Registry catalog
registry/index.jsonone record per schema id

Field reference

33 fields, read from the published schema.json, with names, types, requiredness, and validation as the document states them. The live government form remains the authoritative source.

Fields

  • vehicleCategoryId enum required

    Vehicle Category legend (raw option values as actually submitted): 1 Commercial; 2 Private; 3 Government.

    enum: 1 | 2 | 3
  • vehicleSubCategoryId enum required

    Cascading sub-category, populated client-side via AJAX once vehicleCategoryId is chosen; the static page load renders this select with no

    enum: 9 values
  • oldPlateNumber string optional

    Old Plate Number

  • vehicleMakeId string required

    Disclosed cap: the live dropdown carries 1,569

  • otherVehicleMake string optional

    Free-text vehicle make, used when the desired make is not found in the Vehicle Make list (the list's own last entry is literally 'OTHERS').

  • vehicleModel string required

    Model

  • vehicleColor string optional

    Color

  • fuelType enum required

    Fuel Type

    enum: Petrol | Diesel | Bio-Fuel | Others
  • yearOfManufacture integer required

    The live dropdown lists every year from 1981 through 2027 (47 options); modeled as a bounded integer range rather than a 47-value enum.

    range: 1981–2027
  • engineNumber string optional

    Engine Number

  • insurancePolicyNumber string optional

    Policy Number

  • vehicleTypeId enum required

    Vehicle Type/Group legend (raw option values as actually submitted): 1 Bus; 2 Motor cycle; 3 Pick up; 4 Salon; 5 SUV; 6 Truck; 7 Wagon; 8 Tricycle; 9 Tractor; 10 Crane; 11 Van.

    enum: 11 values
  • chassisNumber string required

    Chassis No

    length: 15–17
  • engineCapacityId enum required

    Engine Capacity legend (raw option values as actually submitted): 1 Below 1.6; 2 Between 1.6 and 2.0; 3 Between 2.1 and 3.0; 4 Above 3.0. Note: the source's own data-val-required message text ('Vehicle Category must be selected') is copy-pasted from the Vehicle Category field above and does not describe this field — a disclosed source authoring artifact, not a GovSchema error.

    enum: 1 | 2 | 3 | 4
  • tankCapacity number required

    Plain numeric text input on the source; no unit is printed on the page itself.

  • odometerReading number optional

    Odometer

  • ownerIdentificationTypeId enum optional

    Owner Identification Type legend (raw option values as actually submitted): 1 Driver's License; 2 National ID Card; 3 International Passport; 4 Company RC Number; 6 Tax Identification Number. Value 5 does not appear on the live page (a gap in the source's own numbering, not a GovSchema omission). Carries no data-val-required attribute on the live page (unlike most other selects in this form) — modeled required:false, a disclosed deviation from a prior scouting pass's assumption that this field was mandatory.

    enum: 1 | 2 | 3 | 4 | 6
  • ownerTitle enum optional

    Confirmed live source defect: the live dropdown visually offers 14 title choices (Mr., Miss, Ms, Mrs., Dr., Prof., Engr., Chief, Rev., Alhaji, Alhaja, Imam, Pastor, Engineer), but the last five

    enum: 9 values
  • ownerFirstName string optional

    Sits in a container (id="identitywithInfo") alongside Title and Last Name, structurally suggesting it is shown for an individual Owner Identification Type and hidden when ownerIdentificationTypeId is Company RC Number (see ownerCompanyName's sibling container). This toggle is inferred from the page's own DOM structure, not confirmed by executing its client-side script, and the field carries no data-val-required attribute on the static page — modeled required:false rather than an asserted, unverified requiredWhen.

    classification: pii
  • ownerLastName string optional

    See ownerFirstName's description for the disclosed individual-vs-company container inference.

    classification: pii
  • ownerCompanyName string optional

    Sits in its own container (id="identitycompanyName"), structurally suggesting it is shown instead of First/Last Name when ownerIdentificationTypeId is Company RC Number. Inferred from DOM structure only, not confirmed by executing client-side script; carries no data-val-required attribute — modeled required:false.

  • ownerIdentificationNumber string required

    Identification No

    classification: sensitive-pii
  • ownerEmail string optional

    Plain text input on the source; no HTML5 type="email", data-val-email, or other format-validation attribute is present, so no pattern constraint is asserted here beyond type.

    classification: pii
  • ownerAddress string optional

    Address

    classification: pii
  • ownerCity string required

    City

    classification: pii
  • ownerMobileNumber string required

    Mobile Number

    classification: pii
  • ownerStateId enum required

    Nigerian state legend (raw option values as actually submitted): 1 Abia; 2 Adamawa; 3 Akwa Ibom; 4 Anambra; 5 Bauchi; 6 Bayelsa; 7 Benue; 8 Borno; 9 Cross River; 10 Delta; 11 Ebonyi; 12 Edo; 13 Ekiti; 14 Enugu; 15 FCT (Federal Capital Territory); 16 Gombe; 17 Imo; 18 Jigawa; 19 Kaduna; 20 Kano; 21 Katsina; 22 Kebbi; 23 Kogi; 24 Kwara; 25 Lagos; 26 Nasarawa; 27 Niger; 28 Ogun; 29 Ondo; 30 Osun; 31 Oyo; 32 Plateau; 33 Rivers; 34 Sokoto; 35 Taraba; 36 Yobe; 37 Zamfara; 38 Federal Government (a non-geographic pseudo-entry the source itself lists alongside the 36 states and the FCT).

    enum: 38 values
  • ownerLgaId string required

    Disclosed cap: the live select carries 780

  • driversLicenseNumber string optional

    Drivers License Number

    classification: sensitive-pii
  • licenseBearerName string optional

    License Bearer Name

    classification: pii
  • plateNumberStateId enum optional

    Same 38-value Nigerian state legend as ownerStateId (see that field's description). Carries no data-val-required attribute on the live page, unlike ownerStateId — modeled required:false.

    enum: 38 values
  • requestFancyNumberPlate boolean optional

    The live page carries a data-val-required attribute on this checkbox, but this is standard ASP.NET Html.CheckBoxFor() boilerplate for a non-nullable bool model property, not a genuine 'must check this box' requirement — the page also renders a paired hidden input of the same name defaulting to "false", the standard MVC pattern that lets an unchecked box still submit a valid, well-formed false. Modeled required:false, a disclosed judgment call overriding the raw attribute.

  • fancyPlateNumber string optional

    The desired fancy/personalized plate text, looked up via the page's own 'Check Available' button. Sits in a container the page itself ids 'IsfancyCheck', strongly implying it is shown (and, once shown, expected) only when requestFancyNumberPlate is checked. Modeled with a visibleWhen/requiredWhen condition on that basis — disclosed as inferred from the container's own naming, not confirmed by executing the page's client-side script.

Verification record

This file is the source-review record for this document version, per the manual-source-review-v1 practice.

Current claim

  • status: draft
  • verification.method: manual-source-review-v1
  • verification.lastVerifiedAt: 2026-07-15

Why this schema and why now (GOV-3227)

GOV-3227 targets Nigeria's last open vertical: DMV. Nigeria already has Passport (ng/nis/application-for-nigeria-standard-passport), Business Formation, Taxes, Visa (ng/nis/application-for-visa-entry-permit), and National ID open (5 of 6). This document opens DMV via the Federal Road Safety Corps' (FRSC) National Vehicle Identification System (NVIS), closing Nigeria to full 6/6 coverage.

The assigning issue's own description was independently re-verified rather than trusted as-is — re-fetched the live page fresh this cycle and read every field/enum/requiredness claim directly from the raw HTML rather than carrying the prior scouting summary forward unchecked. Several of the prior summary's specific counts turned out to be off (see "Corrections to the prior scouting summary" below).

Source

  • URL: https://nvis.frsc.gov.ng/VehicleManagement/RegisterVehicle
  • Retrieved: 2026-07-15T22:11:41Z, fetched directly with curl -L. HTTP/2 200, content-type: text/html; charset=utf-8, server: Kestrel, x-powered-by: ASP.NET. No Location redirect header, no login page, no CAPTCHA/WAF interstitial — the form itself renders directly. A .AspNetCore.Antiforgery.* cookie is set (a CSRF token for the page's own POST, standard ASP.NET behavior for any form, not an auth gate).
  • Byte size: 153,370 bytes.
  • sha256: fe00305563a5fbb3c8259252e8df50b0d8f21ab4e7ca29a57253f24bbf31ab50not independently reproducible on a fresh fetch: the page embeds a fresh __RequestVerificationToken anti-forgery value on every request, so no two fetches ever hash identically even when nothing else changed. Independently re-fetched the page twice, moments apart, and diffed the two responses: byte-identical except for that single token value (one changed line), both containing the same 2,525 <option> elements total. Hashing each response with the __RequestVerificationToken value redacted first yields a stable, reproducible structural sha256 across both fetches: b7ef3870a49b24ea6215cb1aeb68e861e61e558707997ed5938852f16fd32c08. A future re-verification pass should compare against this redacted-hash value, not the raw single-fetch hash above.
  • Confirmed FRSC branding: the page's own nav bar renders /vat-assets/images/logofrsc.jpg, and the nav groups this page ("New Vehicle Registration") alongside sibling flows "Verify Number Plate" and "Vehicle Revalidation", plus a separate "Login" link that is not required to reach this registration form.
  • The page is a plain server-rendered HTML form (ASP.NET MVC, Razor view with unobtrusive client validation), not an AcroForm/PDF — every field below was read directly from the raw HTML's <label>/<input>/ <select>/<textarea> elements and their data-val* attributes, not any rendered/screenshot pass.

Corrections to the prior scouting summary

The assigning issue's field description, re-verified against the live page, needed several corrections:

  • VehicleTypeId: 11 options, not 12. Bus, Crane, Motor cycle, Pick up, Salon, SUV, Tractor, Tricycle, Truck, Van, Wagon — counted directly from the raw <option> elements.
  • EngineCapacityId: 4 bands, not 5. Below 1.6; Between 1.6 and 2.0; Between 2.1 and 3.0; Above 3.0.
  • OwnersTypeId: 5 options, not 6, and with a gap: the live <option value="..."> set is {1, 2, 3, 4, 6} — value 5 does not appear anywhere on the page (a gap in the source's own numbering, not a GovSchema omission).
  • Title: 14 labels are visually offered, but only 9 are genuinely distinct — see the dedicated finding below. The prior summary's count of 15 does not match either number.
  • StateId: 38 options, not 39. Nigeria's 36 states + FCT (Federal Capital Territory, value 15) + a "Federal Government" pseudo-entry (value 38) — counted directly from the raw <option> elements on both stateSelect and VehicleInfoModel_PlateNoStateId.
  • YearOfManufacture: 47 years (1981–2027), not 48.
  • VehicleMakeId and Lgaid counts were confirmed accurate: 1,569 and 780 <option> elements respectively, each counted directly (grep -c "<option" scoped to each <select> block).

None of these corrections change which vertical opens or the overall scope; they are disclosed here because the assigning issue explicitly asked for independent re-verification rather than trust-as-is.

Disclosed-cap fields (per the assigning issue's own guidance)

  • vehicleMakeId (Vehicle Make): 1,569 <option> entries. Critically, the dropdown's own value attribute equals the make name text itself (e.g. <option value=" BMW 5251"> BMW 5251</option>), not an opaque numeric code — so this is genuinely free-text vehicle-make data, not a reference-table ID. The raw list carries visible data-quality noise: stray leading/trailing whitespace and literal tab characters (&#x9;) baked into several values (e.g. " 100&#x9;"), and outright duplicate entries (e.g. "YAMAHA" appears 10 times, "BLUE/BIRD" 6 times). This is a genuine artifact of FRSC's own live reference data, not introduced by this schema. Modeled as type: "string" with a documented note in the field's own description, per the issue's guidance not to enumerate ~1,569 options.
  • ownerLgaId (Local Government): 780 <option> entries. Nigeria has 774 LGAs by common count; the live select's 780 entries likely reflect a few additional non-LGA administrative entries FRSC's own reference table includes (not independently reconciled against an authoritative federal LGA list this cycle — disclosed as unverified). The static page load renders all 780 at once rather than scoping to a single state (no visible AJAX re-population tied to ownerStateId in the raw HTML; whether the live site re-fetches a filtered list client-side after a state is chosen was not executed/confirmed). Modeled as type: "string" with a documented note, per the issue's guidance not to enumerate ~780 options; the field's own description flags that valid values are state-dependent within FRSC's live reference data.

Required-field derivation

Requiredness follows the source's own data-val-required attribute, field-by-field — not a blanket assumption. Fields carrying data-val-required on the live page: CategoryId, SubCategoryId, VehicleMakeId, Model, FuelType, YearOfManufacture, VehicleTypeId, ChasisNo (also data-val-length-min="15"/ data-val-length-max="17"), EngineCapacityId, TankCapacity, IdentificationNo, City, MobileNo, StateId, Lgaid, and the FancyCheck checkbox (see the disclosed override below). That is 15 required: true fields (14 plus the disclosed override on FancyCheck, which nets back out — see below).

Two disclosed judgment calls diverge from a literal mechanical read of data-val-required:

  • requestFancyNumberPlate (the FancyCheck checkbox) carries data-val-required on the live page, but this is modeled required: false. The page also renders a paired hidden input, <input name="VehicleInfoModel.FancyCheck" type="hidden" value="false"> — the standard ASP.NET Html.CheckBoxFor() output for a non-nullable bool model property, which always lets an unchecked box submit a valid, well-formed false. The data-val-required attribute here is MVC scaffolding boilerplate (it is generated automatically for any non-nullable bool property, regardless of whether the checkbox is actually meant to be mandatory), not a genuine "you must check this" business rule. Modeling it as required: false is a disclosed judgment call overriding the raw attribute, not a blind mechanical read.
  • Fields the prior scouting summary assumed required, but which carry no data-val-required attribute on the live page, are modeled required: false: ownerIdentificationTypeId (OwnersTypeId), ownerTitle (Title), ownerFirstName/ownerLastName/ ownerCompanyName, and plateNumberStateId (PlateNoStateId — contrast ownerStateId/StateId, which does carry the attribute). This is a straightforward, disclosed deviation from the prior summary rather than an interpretive call.

Other disclosed judgment calls

  • ownerTitle — confirmed live source defect. The Title dropdown visually offers 14 options: Mr., Miss, Ms, Mrs., Dr., Prof., Engr., Chief, Rev., Alhaji, Alhaja, Imam, Pastor, Engineer. Reading the raw HTML: the last five (Alhaji., Alhaja., Imam., Pastor., Engineer.) each render as <option value="Rev.">Alhaji. </option>, <option value="Rev.">Alhaja. </option>, etc. — their value attribute is the literal string "Rev.", identical to the separately-labeled <option value="Rev.">Rev. </option> immediately before them. A live form submission choosing "Alhaji" from the dropdown would actually POST Title=Rev., indistinguishable server-side from choosing "Rev.", "Alhaja", "Imam", "Pastor", or "Engineer". This schema's ownerTitle enum models the 9 genuinely distinct submittable values (Mr., Miss, Ms, Mrs., Dr., Prof., Engr., Chief, Rev.) rather than fabricating 14 separately-submittable values the live site cannot actually produce. Disclosed in the field's own description as well.
  • ownerFirstName/ownerLastName/ownerCompanyName individual-vs- company inference. The Owner Information panel wraps Title/First Name/Last Name in a container <div id="identitywithInfo"> and Company Name in a separate <div id="identitycompanyName">, structurally suggesting the page's own client-side script toggles between them based on ownerIdentificationTypeId (individual identity types vs. "Company RC Number"). This inference was not confirmed by executing the page's JavaScript this cycle (no browser session was driven against the live site — the field list was read from the static HTML only). Since none of the three fields carry data-val-required regardless, and the toggle condition itself is unconfirmed, all three are modeled required: false with the inference disclosed in each field's own description rather than an asserted requiredWhen this document cannot attribute to confirmed behavior.
  • fancyPlateNumber visibility/requiredness inference. The "Request Fancy Number?" text input + "Check Available" button sit in a container <div id="IsfancyCheck">, strongly suggesting it is shown only once requestFancyNumberPlate is checked. Modeled with visibleWhen/ requiredWhen: { field: "requestFancyNumberPlate", equals: true } on that basis — disclosed as inferred from the container's own naming, not confirmed by executing client-side script.
  • vehicleSubCategoryId is a cascading, AJAX-populated dropdown — independently enumerated via its underlying endpoint. The live page renders <select id="vehicleSubCategoryId" ...> with only its placeholder <option>; its real option set is populated client-side once a Vehicle Category is chosen, by a call to GET /VehicleManagement/GetSubVehicleCategory?vehicelId={id}. That endpoint is itself public and unauthenticated (no cookie/token required beyond what a plain page load already sets), so it does not require driving a browser session — called it directly with curl for all three vehicleCategoryId values and got: vehicelId=1 (Commercial) → {"text":"Motor Cycle","value":"4"}, {"text":"Motor Vehicle","value":"2"}; vehicelId=2 (Private) → {"text":"Motor Cycle","value":"3"}, {"text":"Motor Vehicle","value":"1"}; vehicelId=3 (Government) → {"text":"Diplomatic and Foreign Mission","value":"5"}, {"text":"Federal Parastatals/Agencies/Department","value":"6"}, {"text":"State Ministries/Agencies/Department","value":"7"}, {"text":"Military/Paramilitaries","value":"8"}, {"text":"LGA", "value":"9"}. These 9 raw ids are mutually distinct across all three categories, so vehicleSubCategoryId is modeled as a flat 9-value enum on those ids (matching this schema's raw-submitted-value convention elsewhere) rather than the opaque unenumerated string a prior pass of this schema modeled it as, with the per-category legend and the category-scoping disclosed in the field's own description (v0.3 has no per-enum-value conditional-visibility primitive to encode the scoping itself). All conformance fixtures referencing this field were updated from placeholder values to real derived ids ("1" Motor Vehicle/Private for the Toyota Corolla fixture, "2" Motor Vehicle/Commercial for the MAN truck fixture, propagated to every fixture cloned from either).
  • otherVehicleMake (the "Others" free-text field beside Vehicle Make). No data-val-required attribute; modeled optional. Its relationship to vehicleMakeId (shown when the make is not in the list — the make list's own last entry is literally "OTHERS ") is disclosed in its own description without an asserted visibleWhen, since the exact client-side toggle trigger was not independently confirmed.
  • ownerEmail carries no format-validation attribute at all — no HTML5 type="email", no data-val-email/data-val-regex. Modeled as plain type: "string" with no pattern, faithfully matching the source's own (lack of) validation rather than inventing an email-shape constraint the live form does not itself enforce.
  • No documents[] array. A full-text grep of the raw HTML for type="file", upload, and attach returned zero matches — this form has no file-upload input anywhere. Consistent with the page's own submit button reading "Preview" rather than "Submit"/"Register", implying supporting-document upload (if any) happens at a later step this single-page form does not itself expose.
  • Scope: single-page data-entry form only. The submit button is labeled "Preview" (<button class="find-car" type="submit">... Preview </button>), implying a further confirmation/payment step beyond this page. This schema scopes to the data collected on this page, consistent with GOV-3227's own description of the confirmed live source.

Conformance

An ephemeral, from-scratch conformance checker (deriving required/ requiredWhen/visibleWhen/pattern/minLength/maxLength/enum/range rules directly from this schema's own fields[], discarded after use, not committed) ran the following fixtures in conformance/ng/frsc/vehicle-registration/1.0.0/:

  • valid-toyota-lagos-individual.json (an individual owner in Lagos registering a used Toyota, National ID Card identification, no fancy plate request) — 0 errors.
  • valid-company-abuja-fancy-plate.json (a company owner in the FCT registering a commercial truck, Company RC Number identification, with requestFancyNumberPlate: true and a populated fancyPlateNumber) — 0 errors.
  • mutation-control-missing-vehicle-category.json (drops vehicleCategoryId) — exactly 1 error.
  • mutation-control-missing-vehicle-sub-category.json (drops vehicleSubCategoryId) — exactly 1 error.
  • mutation-control-missing-vehicle-make.json (drops vehicleMakeId) — exactly 1 error.
  • mutation-control-missing-vehicle-model.json (drops vehicleModel) — exactly 1 error.
  • mutation-control-missing-fuel-type.json (drops fuelType) — exactly 1 error.
  • mutation-control-missing-year-of-manufacture.json (drops yearOfManufacture) — exactly 1 error.
  • mutation-control-invalid-year-of-manufacture-range.json (sets yearOfManufacture to 1975, below the minimum: 1981 bound) — exactly 1 error.
  • mutation-control-missing-vehicle-type.json (drops vehicleTypeId) — exactly 1 error.
  • mutation-control-invalid-chassis-number-length.json (sets chassisNumber to a 10-character string, below minLength: 15) — exactly 1 error.
  • mutation-control-missing-engine-capacity.json (drops engineCapacityId) — exactly 1 error.
  • mutation-control-missing-tank-capacity.json (drops tankCapacity) — exactly 1 error.
  • mutation-control-missing-identification-number.json (drops ownerIdentificationNumber) — exactly 1 error.
  • mutation-control-missing-city.json (drops ownerCity) — exactly 1 error.
  • mutation-control-missing-mobile-number.json (drops ownerMobileNumber) — exactly 1 error.
  • mutation-control-missing-state.json (drops ownerStateId) — exactly 1 error.
  • mutation-control-missing-lga.json (drops ownerLgaId) — exactly 1 error.
  • mutation-control-fancy-plate-requested-but-number-missing.json (sets requestFancyNumberPlate: true but omits fancyPlateNumber, exercising requiredWhen) — exactly 1 error.

19 fixtures total (2 valid, 17 mutation-control).

Structural validation

  • node tools/validate.mjs and node tools/validate-ajv.mjs (ajv 2020-12 against spec/v0.3) both pass on the full registry after adding this document: 491/491 (up from 490/490).
  • node tools/verify-sources.mjs run clean against the new document's source URL.
  • npm run build-index re-run in tools/govschema-client/ to regenerate registry-index.json with this document included (491 entries).

Maturity

structural-reference: the source form's own printed field labels, data-val-required/data-val-length attributes, and <option> legend values are fully transcribed from the genuine, currently-served live NVIS page, but no live filing (submitting the form and observing the resulting "Preview"/payment flow) was attempted. GovSchema is an independent, non-profit standards body and is not affiliated with, endorsed by, or operated by the Government of the Federal Republic of Nigeria or the Federal Road Safety Corps.

View the raw record (VERIFICATION.md)

Version history

  • 1.0.0 draft latest this page has verification record schema.json

Independent and non-affiliated

GovSchema is an independent, open-source project. This reference is not produced, reviewed, or endorsed by Federal Road Safety Corps or any government. The authoritative source is always the live government form and its official instructions.