Build a Connected Fleet Risk API

Build a FastAPI that validates fleet telemetry and returns risk assessments.

Introduction

30 Second Summary

When vehicle data arrives in different shapes, a fleet safety team can waste valuable time deciding what it means. Impossible measurements can also lead to poor operational decisions.

In this project, you will build a local REST API with FastAPI for a fictional enterprise customer. It validates vehicle telemetry before turning each request into an explainable risk assessment.

What You'll Build

In Microsoft Edge, you will submit telemetry through interactive browser documentation to reveal a vehicle's risk score, rating, contributing factors, and recommended action.

By the end of this project, you'll have:

  • A live browser demo that shows low, medium, and high risk outcomes from prepared vehicle telemetry.
  • A set of validation tests that reject impossible measurements before they reach the risk engine.
  • An interview-ready POC brief that connects customer discovery, solution architecture, measurable success criteria, and a five-minute demo narrative.
  • Secret Mission: Add a batch endpoint that assesses multiple vehicles in one request.

Are there any prerequisites?

You need Windows 10 or newer. No prior FastAPI experience is required because this guide covers every required setup.

Before We Start

Northstar Logistics receives vehicle telemetry from multiple providers. This opening checkpoint locks in the need for a validated API contract that produces explainable risk actions before the hands-on work begins.

Set Up the Windows Workspace

Northstar Logistics needs a reproducible workspace before its fleet-risk idea becomes a customer-facing API. A consistent local environment removes installation uncertainty from the POC.

You will prepare Python inside Visual Studio Code. You will isolate the project in a virtual environment with pinned FastAPI plus Pydantic dependencies.

In this step, get ready to:
  • Install the Windows development tools required by the local POC.
  • Create the project workspace with an isolated Python environment.
  • Install the pinned API dependencies from requirements.txt.
Install and verify the Windows tools

Python executes the project code. The Visual Studio Code Python extension connects the editor to your selected interpreter.

Python 3.15.2 requires Windows 10 or newer. Microsoft Edge provides the browser you will use for API testing later.

  • Press the Windows key to open search.
  • Type Command Prompt into the search box.
  • Press Enter to open Command Prompt.
  • Check the installed Python 3 version by running this command:
py -3 --version

What Does This Command Check?

  • The py command asks the Windows Python launcher for an installed runtime.
  • The -3 option selects the latest available Python 3 runtime.
  • The output identifies the version that Windows can launch.

✔️ I see Python 3.15.2

Your output shows Python 3.15.2. The required runtime is ready.

ⓧ I see an older version

Your current Python runtime is below the project version. Python supports multiple installed runtimes on Windows.

  • Open the official Python Windows downloads page in Microsoft Edge.
  • Find Python 3.15.2 under the stable releases.
  • Select Download Windows installer (64-bit).
  • Run the downloaded installer from your Downloads folder.
  • Select Install Now in the installer.
  • Close Command Prompt after the installation finishes.
  • Repeat the version check in a fresh Command Prompt window.

ⓧ Command not found

Windows cannot find the Python launcher yet. The official installer adds the runtime plus the launcher required by this project.

  • Open the official Python Windows downloads page in Microsoft Edge.
  • Find Python 3.15.2 under the stable releases.
  • Select Download Windows installer (64-bit).
  • Run the downloaded installer from your Downloads folder.
  • Select Install Now in the installer.
  • Close Command Prompt after the installation finishes.
  • Repeat the version check in a fresh Command Prompt window.

Python is available to Windows. Next, confirm that your code editor is available.

  • Press the Windows key to open search.
  • Type Visual Studio Code into the search box.
  • Press Enter to test whether Visual Studio Code opens.

✔️ Visual Studio Code opens

Visual Studio Code opens successfully. Keep this window open for the extension check.

ⓧ Visual Studio Code is missing

The Windows User setup installs Visual Studio Code for your account. The installer presents license terms plus setup options before installation.

  • Open the official Visual Studio Code download page in Microsoft Edge.
  • Select the Windows User setup download.
  • Run the downloaded installer from your Downloads folder.
  • Complete the installer with its default setup options.
  • Press the Windows key after installation.
  • Type Visual Studio Code into the search box.
  • Press Enter to open Visual Studio Code.
  • Select the Extensions icon in the Visual Studio Code Activity Bar.
  • Type Python into the Extensions search box.
  • Select the Python extension published by Microsoft.

✔️ Manage gear is shown

The Manage gear confirms that the Python extension is installed.

ⓧ Install is shown

The Python extension still needs to be added to Visual Studio Code.

  • Select Install on the Python extension page.
  • Wait for the button to change to the Manage gear.
  • Close Visual Studio Code so it can refresh its Python detection.
  • Press the Windows key to open search.
  • Type Visual Studio Code into the search box.
  • Press Enter to restart Visual Studio Code.
  • Press the Windows key again.
  • Type Microsoft Edge into the search box.
  • Press Enter to confirm that Microsoft Edge opens.

Microsoft Edge opens successfully. Your browser is ready for the interactive API documentation in the next step.

Is a Windows Tool Still Missing?

If Python still reports an older version, confirm that you downloaded the installer listed beneath Python 3.15.2. Restart the app where you ran the check.

If Microsoft Edge is missing, use another browser to visit the official Microsoft Edge download page. Install the Windows version before continuing.

If the Python extension does not install, confirm that Visual Studio Code can access the internet. Help me diagnose my Windows tool setup.

Create the workspace and virtual environment

A dedicated workspace keeps every POC file in one named location. Its .venv environment isolates this project's packages from other Python projects.

  • Press the Windows key to open search.
  • Type File Explorer into the search box.
  • Press Enter to open File Explorer.
  • Select Desktop in the File Explorer sidebar.
  • Select New in the toolbar.
  • Select Folder.
  • Type connected-fleet-risk-api as the folder name.
  • Press Enter to create the folder.

You can now see connected-fleet-risk-api on your Desktop. This folder becomes the single workspace for the POC.

  • Switch back to Visual Studio Code.
  • Select File in the top menu bar.
  • Select Open Folder....
  • Select Desktop in the folder picker.
  • Select the connected-fleet-risk-api folder.
  • Confirm your folder choice.

The Explorer sidebar now shows connected-fleet-risk-api as the open workspace.

  • Press Ctrl+Shift+P to open the Command Palette.
  • Type Python: Create Environment into the Command Palette.
  • Select Python: Create Environment from the results.
  • Select Venv as the environment type.
  • Select the Python 3.15.2 interpreter.

Environment creation can take a short moment. You will see a .venv folder in the Explorer sidebar when it finishes.

  • Press Ctrl+Shift+P to return to the Command Palette.
  • Type Python: Select Interpreter into the Command Palette.
  • Select Python: Select Interpreter from the results.
  • Select the Python 3.15.2 interpreter inside .venv.
  • Press Ctrl+Shift+P once more.
  • Type Terminal: Create New Terminal into the Command Palette.
  • Select Terminal: Create New Terminal from the results.

The integrated terminal opens for the selected .venv interpreter. New package installations now stay inside this project.

Why Use a Virtual Environment?

A virtual environment gives the project its own package location. This prevents another Python project from changing the versions your POC depends on.

Selecting the environment in Visual Studio Code also connects editor features to the same interpreter used by the terminal. Your code plus your tools now share one runtime.

🙋‍♀️ Do You Not See .venv?

Confirm that connected-fleet-risk-api is the folder shown at the top of the Explorer sidebar. Creating the environment in another open folder places .venv in the wrong workspace.

Run Python: Select Interpreter again if the integrated terminal uses a different interpreter. Choose the entry whose path includes .venv.

Help me fix my virtual environment.

Pin and verify the dependencies

The requirements.txt file records the exact package versions for this POC. Anyone who installs from this file receives the same FastAPI plus Pydantic versions.

  • Select the New File button in the Visual Studio Code Explorer toolbar.
  • Type requirements.txt as the file name.
  • Press Enter to create the file.
  • Paste these pinned dependencies into requirements.txt:
fastapi[standard-no-fastapi-cloud-cli]==0.162.0
pydantic==2.14.1

What Does This File Control?

  • The FastAPI requirement installs version 0.162.0 with its standard local development dependencies.
  • The selected extra excludes the FastAPI Cloud CLI from this local project.
  • The Pydantic requirement installs version 2.14.1 for the validated request models you will add later.
  • Press Ctrl+S to save requirements.txt.
  • Confirm that the file contains exactly two dependency lines.

Does the File Look Different?

Remove any blank dependency entries or extra package lines. Confirm that both version pins use two equals signs.

Help me compare my requirements file.

✔️ Awesome, I've got everything!

Great. Your saved requirements.txt file now pins both project dependencies.

ⓧ I'd like to double check the full code

fastapi[standard-no-fastapi-cloud-cli]==0.162.0
pydantic==2.14.1

The selected virtual environment is ready to receive the packages listed in requirements.txt.

  • Return to the integrated terminal created for .venv.
  • Install the pinned project dependencies by running this command:
pip install -r requirements.txt

What Does This Command Do?

  • The pip command installs Python packages into the active environment.
  • The -r option tells pip to read package requirements from a file.
  • The version pins keep the local POC reproducible.

The terminal downloads the required packages. Control returns to the prompt when installation finishes.

Does the Installation Fail?

Confirm that the terminal belongs to the selected .venv environment. A terminal opened before interpreter selection can use a different Python installation.

Check that requirements.txt is visible in the Explorer sidebar. The install command reads it from the open connected-fleet-risk-api folder.

Help me diagnose the dependency installation.

Verify the reproducible workspace

Before you run the final checks, which Python version plus package pins do you expect the terminal to report?

  • Verify the Windows Python runtime plus the installed environment packages by running these checks:
py -3 --version
pip freeze

What Do These Checks Prove?

  • The first command confirms that Windows can launch the required Python 3 runtime.
  • The second command lists the packages installed in the active virtual environment.
  • The combined output verifies both the runtime plus the pinned API dependencies.

You will see Python 3.15.2 in the first result. The package list includes fastapi==0.162.0 plus pydantic==2.14.1.

That is the setup uncertainty cleared. Your isolated workspace is ready for the first running fleet telemetry endpoint.

Launch a Raw Telemetry API

Your pinned workspace removes setup uncertainty. The next proof point is a browser-visible API that a customer can test.

FastAPI exposes routes through interactive documentation in Microsoft Edge. A raw dictionary keeps the first endpoint small while you test what happens to impossible telemetry.

In this step, get ready to:
  • Build a FastAPI application with a root status route.
  • Add a raw assessment route that accepts a dictionary.
  • Compare normal telemetry with impossible negative values.
Create and launch the FastAPI application

The app object is the FastAPI application that the development server discovers. Its metadata gives the documentation a customer-facing identity.

  • Select the new-file icon beside connected-fleet-risk-api in the file sidebar of Visual Studio Code.
  • Enter main.py as the file name.

You will see main.py inside the connected-fleet-risk-api folder.

  • Add the application setup to main.py by entering this code:
from fastapi import FastAPI

app = FastAPI(
    title="Connected Fleet Risk API",
    description=(
        "Vendor-neutral fleet telemetry risk POC. "
        "The scoring rules are illustrative and are not a production safety policy."
    ),
    version="1.0.0",
)

What Does This Code Do?

  • FastAPI(...) creates the application instance that the server discovers.
  • title names the service in its OpenAPI documentation.
  • description records the boundary between this POC and a production safety policy.
  • version labels this API contract as 1.0.0.
  • Save main.py.

The development command searches main.py for the app object. It keeps the integrated terminal busy while the server runs.

  • Start the development server from the active integrated terminal by running this command:
fastapi dev

What Does This Command Do?

The command starts FastAPI in development mode. It also watches main.py for saved changes.

The server makes the application available at http://127.0.0.1:8000. Its interactive documentation lives at http://127.0.0.1:8000/docs.

  • Press the Windows key to open the search bar.
  • Type Microsoft Edge into the search bar.
  • Press Enter to open Microsoft Edge.
  • Enter http://127.0.0.1:8000/docs in the address bar.
  • Load the address.

You will see interactive documentation titled Connected Fleet Risk API. The page proves that your local server is responding.

Server Not Starting?

  • Confirm that the integrated terminal is inside the connected-fleet-risk-api folder.
  • Confirm that the selected interpreter belongs to .venv.
  • Confirm that main.py contains the app object shown above.

Help me diagnose the FastAPI development server.

Add the raw telemetry routes

A route connects an HTTP path to a Python function. The root route gives you a quick status response before telemetry enters the application.

  • Add the root route beneath the app setup in main.py by entering this code:
@app.get("/")
def read_root() -> dict[str, str]:
    return {
        "message": "Connected Fleet Risk API is running",
        "documentation": "/docs",
    }

What Does This Route Do?

  • @app.get("/") connects a GET request at the root path to read_root().
  • read_root() returns a dictionary containing a status message.
  • documentation points a reviewer toward /docs.
  • Save main.py.
  • Switch back to Microsoft Edge.
  • Enter http://127.0.0.1:8000 in the address bar.
  • Load the address.

You will see a response containing Connected Fleet Risk API is running. You will also see the documentation path.

Root Route Not Responding?

  • Confirm that you saved main.py after adding read_root().
  • Confirm that the terminal still shows the running development server.
  • Check that the route decorator contains the root path /.

Help me fix the root route.

The assessment route uses POST because the caller sends telemetry in a request body. A plain dict accepts the body without declaring individual telemetry fields.

  • Add the raw assessment route below read_root() in main.py by entering this code:
@app.post("/assess-risk")
def assess_risk(telemetry: dict) -> dict:
    return telemetry

What Does This Route Do?

  • @app.post("/assess-risk") exposes the risk-assessment path as a POST operation.
  • telemetry: dict receives the request body as a Python dictionary.
  • return telemetry echoes the submitted body so you can inspect exactly what the route accepted.
  • Save main.py.
  • Switch back to Microsoft Edge.
  • Return to http://127.0.0.1:8000/docs.
  • Reload the documentation using the browser reload control.

You will see a separate POST /assess-risk operation. The running server reloaded your saved route.

Assessment Route Missing?

  • Confirm that assess_risk() appears below read_root() in main.py.
  • Check that the path is spelled /assess-risk.
  • Reload the documentation after saving the file.

Help me find my assessment route.

✔️ Awesome, I've got everything!

Your saved main.py now contains the application metadata plus both raw routes.

ⓧ I'd like to double check the full code

  • Compare your saved main.py with this complete reference:
from fastapi import FastAPI

app = FastAPI(
    title="Connected Fleet Risk API",
    description=(
        "Vendor-neutral fleet telemetry risk POC. "
        "The scoring rules are illustrative and are not a production safety policy."
    ),
    version="1.0.0",
)


@app.get("/")
def read_root() -> dict[str, str]:
    return {
        "message": "Connected Fleet Risk API is running",
        "documentation": "/docs",
    }


@app.post("/assess-risk")
def assess_risk(telemetry: dict) -> dict:
    return telemetry

What Should Match?

The metadata appears first. The root route follows the application setup.

The raw assessment route appears last. Its telemetry parameter remains a plain dict for this designed test.

Test impossible telemetry

A JSON request body represents telemetry as named values. Your first request establishes what a normal submission looks like.

Normal Telemetry Values

  • Use fleet-101 for vehicle_id.
  • Use 65 for speed_kph.
  • Use 0 for harsh_braking_events.
  • Use true for seatbelt_fastened.
  • Expand POST /assess-risk in the interactive documentation.
  • Enable request editing with the control beside the request body editor.

The request body editor is now editable. It accepts a JSON object for the raw dictionary.

  • Clear the generated example from the request body editor.
  • Enter a JSON object using the normal telemetry values above.
  • Select Execute to send the normal request.

Good progress. The response body echoes the same vehicle ID plus the three telemetry values.

Negative speed plus negative braking counts are impossible measurements. The next request probes how the raw endpoint handles them.

  • Change speed_kph to -25 in the request body editor.
  • Change harsh_braking_events to -2 in the request body editor.

Before you run this request, do you expect the raw endpoint to reject the impossible measurements or return them?

  • Select Execute to send the impossible telemetry.

You will see the response echo speed_kph as -25 plus harsh_braking_events as -2.

That shortfall is intentional. No field contract stands between the request body and the endpoint.

Request Not Echoing the Negative Values?

  • Check that the request editor contains valid JSON punctuation.
  • Confirm that main.py contains def assess_risk(telemetry: dict) -> dict:.
  • Confirm that you executed the POST /assess-risk operation.

Help me debug the raw telemetry request.

You exposed the exact weakness this POC needs to solve. Next, you will turn the loose dictionary into a validated telemetry contract.

Validate Vehicle Telemetry

Your raw FastAPI endpoint is live. Its acceptance of impossible negative measurements exposed the risk of using a plain dictionary.

A Pydantic model now becomes the gatekeeper before your endpoint logic runs. Its constraints turn vehicle telemetry into an API contract that rejects malformed values.

In this step, get ready to:
  • Define a constrained vehicle telemetry model.
  • Apply the typed model to the risk endpoint.
  • Prove that invalid telemetry fails while valid telemetry succeeds.
Define the telemetry contract

A request model declares the fields that every telemetry body must contain. The Field constraints set the accepted length or numeric range for each value.

  • Switch back to main.py in Visual Studio Code.
  • Find from fastapi import FastAPI at the top of the file.
  • Select from that import through the closing parenthesis below version="1.0.0",.
  • Replace the selected section by pasting this code:
from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI(
    title="Connected Fleet Risk API",
    description=(
        "Vendor-neutral fleet telemetry risk POC. "
        "The scoring rules are illustrative and are not a production safety policy."
    ),
    version="1.0.0",
)


class VehicleTelemetry(BaseModel):
    vehicle_id: str = Field(min_length=1, max_length=40)
    speed_kph: float = Field(ge=0, le=200)
    harsh_braking_events: int = Field(ge=0, le=50)
    seatbelt_fastened: bool

What Does This Code Do?

  • The BaseModel class gives incoming telemetry a declared structure.
  • The min_length constraint prevents an empty vehicle identifier.
  • The max_length constraint caps the identifier at 40 characters.
  • The ge constraints reject negative speed or braking values.
  • The le constraints reject values above the illustrative POC limits.
  • The seatbelt_fastened field declares the seatbelt state as a Boolean value.
  • Save main.py.
  • Return to the terminal running fastapi dev.

The development server reloads the application. You should see it continue running without a Python traceback.

Does the Server Stop Reloading?

Confirm that VehicleTelemetry appears after the FastAPI application definition. Check that each opening parenthesis has a matching closing parenthesis.

Confirm that the Pydantic import matches the code above. A misspelled class or field name prevents the module from loading.

Help me fix the telemetry model in main.py.

Apply the contract to the endpoint

FastAPI validates a request against the type named in the endpoint signature. Replacing dict with VehicleTelemetry activates the constraints before assess_risk() receives the request.

  • Find the assess_risk() function at the bottom of main.py.
  • Replace the complete function plus its decorator with this typed version:
@app.post("/assess-risk")
def assess_risk(telemetry: VehicleTelemetry) -> VehicleTelemetry:
    return telemetry

How Does the Contract Reach the Endpoint?

  • The telemetry: VehicleTelemetry annotation tells FastAPI to validate the request body against your model.
  • The VehicleTelemetry return type documents the shape of the echoed response.
  • The return telemetry line keeps the endpoint simple while you test the new boundary.
  • Save main.py.
  • Switch back to Microsoft Edge.
  • Refresh http://127.0.0.1:8000/docs.
  • Expand the POST /assess-risk endpoint.

You should see a request schema with vehicle_id, speed_kph, harsh_braking_events, plus seatbelt_fastened.

Still Seeing an Unrestricted Request Body?

Confirm that the parameter type is VehicleTelemetry. A remaining dict annotation keeps the old unrestricted schema.

Confirm that the server reloaded after you saved the file. Refresh the documentation after the reload completes.

Help me update the endpoint schema.

✔️ Awesome, I've got everything!

Great. Your endpoint now uses the constrained VehicleTelemetry contract.

ⓧ I'd like to double check the full code

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI(
    title="Connected Fleet Risk API",
    description=(
        "Vendor-neutral fleet telemetry risk POC. "
        "The scoring rules are illustrative and are not a production safety policy."
    ),
    version="1.0.0",
)


class VehicleTelemetry(BaseModel):
    vehicle_id: str = Field(min_length=1, max_length=40)
    speed_kph: float = Field(ge=0, le=200)
    harsh_braking_events: int = Field(ge=0, le=50)
    seatbelt_fastened: bool


@app.get("/")
def read_root() -> dict[str, str]:
    return {
        "message": "Connected Fleet Risk API is running",
        "documentation": "/docs",
    }


@app.post("/assess-risk")
def assess_risk(telemetry: VehicleTelemetry) -> VehicleTelemetry:
    return telemetry

How Does the File Fit Together?

The model sits between the application metadata plus the route functions. The typed endpoint uses that model for both the request body and the echoed response.

Test the validation boundary

The generated OpenAPI documentation sends requests through the same validation path as any client. You can now repeat the malformed request that the raw dictionary accepted.

  • Return to the request body editor you used earlier.
  • Enter fleet-017 for vehicle_id.
  • Set speed_kph to -25.
  • Set harsh_braking_events to 2.
  • Set seatbelt_fastened to true.

Before you submit the request, do you expect the endpoint to echo the negative speed again?

  • Submit the invalid telemetry request.

The response reports a detailed validation error for speed_kph. The malformed body never reaches the endpoint logic.

Does the Negative Speed Still Succeed?

Confirm that assess_risk() accepts VehicleTelemetry. Confirm that speed_kph uses Field(ge=0, le=200).

Save main.py before refreshing the documentation. The browser can keep an older schema until you refresh it.

Help me find why invalid speed is accepted.

  • Change speed_kph from -25 to 65.

Before you submit again, do you expect all four telemetry fields to pass the contract?

  • Submit the corrected telemetry request.

The request succeeds. The response echoes fleet-017 with a speed of 65 plus the other accepted values.

That closes the gap from the raw endpoint. Malformed measurements now stop at the API boundary.

Your endpoint now trusts the shape of every request it receives. Next, you will turn that validated telemetry into a score plus an operational recommendation.

Calculate Risk and Recommend Action

Your FastAPI endpoint now uses a Pydantic contract to block impossible fleet telemetry. That gives your POC trusted input.

Trusted input still does not answer the customer's operational question. You will add deterministic risk scoring that produces an explainable rating plus a recommended action.

In this step, get ready to:
  • Define the structured risk-assessment response.
  • Calculate risk from speed, braking events, and seatbelt status.
  • Test low, medium, and high risk scenarios in the browser.
Define the response contract

A response model gives every successful assessment the same structure. The Literal type restricts the rating to the three values your POC supports.

  • In Visual Studio Code, select main.py in the Explorer sidebar.
  • Find the import group at the top of main.py.
  • Replace the current import group with this expanded import group:
from typing import Literal

from fastapi import FastAPI
from pydantic import BaseModel, Field

What Does This Import Add?

  • The Literal type lets the response contract list its permitted rating values.
  • The existing FastAPI plus Pydantic imports remain available to the application.
  • Save main.py.
  • Return to the integrated terminal from earlier.

The development server reloads without a Python traceback. This confirms that the expanded import group is valid.

Does the Server Stop Reloading?

Check that Literal uses a capital L. Confirm that it is imported from typing.

Help me fix the import error.

  • Find the blank space between VehicleTelemetry and @app.get("/").
  • Add the response model in that space by pasting this code:
class RiskAssessment(BaseModel):
    vehicle_id: str
    risk_score: int
    risk_rating: Literal["low", "medium", "high"]
    risk_factors: list[str]
    recommendation: str

What Does This Model Define?

  • The vehicle_id field connects the assessment to its source vehicle.
  • The risk_score plus risk_rating fields summarize the result.
  • The risk_factors field explains which rules contributed to the score.
  • The recommendation field turns the assessment into an operational next step.
  • Save main.py.
  • Return to the integrated terminal from earlier.

The server reloads without a traceback. Your API now has a structured output contract for every assessment.

Seeing a Model Error?

Confirm that RiskAssessment inherits from BaseModel. Check that each field remains indented inside the class.

Help me fix the response model.

Build the scoring function

The scoring function keeps the POC rules in one reusable place. Each matching condition adds points plus an explanation to the assessment.

  • Find the blank space between RiskAssessment and @app.get("/").
  • Add the speed plus braking rules by pasting this first part of the function:
def calculate_risk(telemetry: VehicleTelemetry) -> RiskAssessment:
    risk_score = 0
    risk_factors: list[str] = []

    if telemetry.speed_kph >= 100:
        risk_score += 2
        risk_factors.append("High speed detected")
    elif telemetry.speed_kph >= 80:
        risk_score += 1
        risk_factors.append("Elevated speed detected")

    if telemetry.harsh_braking_events >= 3:
        risk_score += 2
        risk_factors.append("Repeated harsh braking detected")
    elif telemetry.harsh_braking_events >= 1:
        risk_score += 1
        risk_factors.append("Harsh braking detected")

How Do These Rules Build the Score?

  • The risk_score value starts at 0 before any conditions are checked.
  • The risk_factors list records a plain-language reason for every point-producing rule.
  • The speed conditions award one or two points according to the matched range.
  • The braking conditions award one or two points according to the event count.
  • Save main.py.
  • Return to the integrated terminal from earlier.

The server reloads without a traceback. This confirms that the speed plus braking branches have valid Python syntax.

Does the First Function Chunk Fail?

Check that every elif lines up with its matching if. Confirm that the score updates plus factor entries remain indented inside their conditions.

Help me fix the scoring branches.

  • Place your cursor below the final risk_factors.append("Harsh braking detected") line.
  • Complete calculate_risk() by adding the remaining code:
    if not telemetry.seatbelt_fastened:
        risk_score += 3
        risk_factors.append("Seatbelt is not fastened")

    if risk_score >= 4:
        risk_rating = "high"
        recommendation = "Escalate for an immediate fleet safety review."
    elif risk_score >= 2:
        risk_rating = "medium"
        recommendation = "Review the trip and coach the driver on flagged behaviors."
    else:
        risk_rating = "low"
        recommendation = "Continue normal monitoring."

    if not risk_factors:
        risk_factors.append("No configured risk factors detected")

    return RiskAssessment(
        vehicle_id=telemetry.vehicle_id,
        risk_score=risk_score,
        risk_rating=risk_rating,
        risk_factors=risk_factors,
        recommendation=recommendation,
    )

How Does the Score Become an Action?

  • An unfastened seatbelt adds three points plus its own risk factor.
  • A score of four or more maps to a high rating plus an immediate review recommendation.
  • A score of two or more maps to a medium rating plus a coaching recommendation.
  • Any lower score maps to a low rating plus normal monitoring.
  • A request with no triggered rules still receives an explanatory factor.

💡 These Are Illustrative POC Rules

The thresholds demonstrate transparent scoring for a fictional customer. They are not regulatory guidance or a production safety policy.

  • Save main.py.
  • Return to the integrated terminal from earlier.

The server reloads without a traceback. Your reusable scoring function can now build a complete RiskAssessment.

Does the Completed Function Fail?

Confirm that the seatbelt condition plus rating branches remain inside calculate_risk(). Check that the closing parenthesis for RiskAssessment() lines up with the return statement.

Help me fix the completed risk function.

Connect and test the risk endpoint

The endpoint currently returns validated telemetry unchanged. Connecting it to calculate_risk() turns the same request into the customer's operational assessment.

  • Find the existing assess_risk() function at the bottom of main.py.
  • Replace the entire endpoint function with this version:
@app.post("/assess-risk")
def assess_risk(telemetry: VehicleTelemetry) -> RiskAssessment:
    return calculate_risk(telemetry)

What Changes at the Endpoint?

  • The request still passes through the VehicleTelemetry validation contract.
  • The return type now documents plus validates the structured RiskAssessment response.
  • The endpoint delegates every scoring decision to calculate_risk().
  • Save main.py.
  • Return to the integrated terminal from earlier.

The server reloads without a traceback. The endpoint is ready to return scored assessments.

✔️ Awesome, I've got everything!

Great. Double-check that main.py is saved before testing the three scenarios.

ⓧ I'd like to double check the full code

from typing import Literal

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI(
    title="Connected Fleet Risk API",
    description=(
        "Vendor-neutral fleet telemetry risk POC. "
        "The scoring rules are illustrative and are not a production safety policy."
    ),
    version="1.0.0",
)


class VehicleTelemetry(BaseModel):
    vehicle_id: str = Field(min_length=1, max_length=40)
    speed_kph: float = Field(ge=0, le=200)
    harsh_braking_events: int = Field(ge=0, le=50)
    seatbelt_fastened: bool


class RiskAssessment(BaseModel):
    vehicle_id: str
    risk_score: int
    risk_rating: Literal["low", "medium", "high"]
    risk_factors: list[str]
    recommendation: str


def calculate_risk(telemetry: VehicleTelemetry) -> RiskAssessment:
    risk_score = 0
    risk_factors: list[str] = []

    if telemetry.speed_kph >= 100:
        risk_score += 2
        risk_factors.append("High speed detected")
    elif telemetry.speed_kph >= 80:
        risk_score += 1
        risk_factors.append("Elevated speed detected")

    if telemetry.harsh_braking_events >= 3:
        risk_score += 2
        risk_factors.append("Repeated harsh braking detected")
    elif telemetry.harsh_braking_events >= 1:
        risk_score += 1
        risk_factors.append("Harsh braking detected")

    if not telemetry.seatbelt_fastened:
        risk_score += 3
        risk_factors.append("Seatbelt is not fastened")

    if risk_score >= 4:
        risk_rating = "high"
        recommendation = "Escalate for an immediate fleet safety review."
    elif risk_score >= 2:
        risk_rating = "medium"
        recommendation = "Review the trip and coach the driver on flagged behaviors."
    else:
        risk_rating = "low"
        recommendation = "Continue normal monitoring."

    if not risk_factors:
        risk_factors.append("No configured risk factors detected")

    return RiskAssessment(
        vehicle_id=telemetry.vehicle_id,
        risk_score=risk_score,
        risk_rating=risk_rating,
        risk_factors=risk_factors,
        recommendation=recommendation,
    )


@app.get("/")
def read_root() -> dict[str, str]:
    return {
        "message": "Connected Fleet Risk API is running",
        "documentation": "/docs",
    }


@app.post("/assess-risk")
def assess_risk(telemetry: VehicleTelemetry) -> RiskAssessment:
    return calculate_risk(telemetry)

How to Compare the File

This reference shows the exact cumulative main.py file for this step. Compare the imports, models, scoring function, and route order.

The interactive documentation lets you reuse one JSON request body across three scenarios. Each response shows how the same contract produces a different operational outcome.

  • Refresh http://127.0.0.1:8000/docs in Microsoft Edge from earlier.
  • Expand POST /assess-risk.
  • Select the control that makes the request body editable.
  • Set vehicle_id to fleet-low-01.
  • Set speed_kph to 65.
  • Set harsh_braking_events to 0.
  • Set seatbelt_fastened to true.
  • Select Execute.

The low-risk response shows a score of 0 plus a rating of low. It recommends normal monitoring.

  • Set vehicle_id to fleet-medium-01.
  • Set speed_kph to 85.
  • Set harsh_braking_events to 1.
  • Set seatbelt_fastened to true.
  • Select Execute.

The medium-risk response shows a score of 2 plus a rating of medium. Its factors identify elevated speed plus harsh braking.

  • Set vehicle_id to fleet-high-01.
  • Set speed_kph to 112.
  • Set harsh_braking_events to 4.
  • Set seatbelt_fastened to false.

Before you execute the final request, ask yourself which rules you expect to contribute to the score.

  • Select Execute.

The high-risk response shows a score of 7 plus a rating of high.

You see three risk factors. The recommendation says Escalate for an immediate fleet safety review..

You now have a risk engine that turns trusted telemetry into an operational decision.

Do You Still See Raw Telemetry?

Confirm that assess_risk() returns calculate_risk(telemetry). Save main.py before refreshing the documentation.

Help me diagnose the unexpected response.

Your API now turns validated fleet telemetry into an explainable score plus a recommended action. Next, you will package the working POC as a customer-focused solutions engineering story.

Package the POC Story

Your FastAPI risk engine now turns validated telemetry into an operational answer. The high-risk test proves that your local API can support a live customer conversation.

A working endpoint proves your coding ability. A solutions engineering artifact also explains why the customer should care.

This step packages customer discovery into a portfolio brief. The same brief gives you a credible five-minute demo flow.

In this step, get ready to:
  • Document the fictional customer problem plus the POC boundaries.
  • Map the vendor-neutral architecture to measurable success criteria.
  • Rehearse a five-minute story around the live high-risk response.
Write the customer discovery brief

A discovery brief gives the POC a named customer plus a primary stakeholder. It also records the customer question that the API must answer.

The assumptions establish that telemetry arrives as JSON. They also protect the local scope from production expectations.

  • Create portfolio-brief.md inside connected-fleet-risk-api with the file-creation control in the Visual Studio Code file sidebar.

You should see an empty portfolio-brief.md tab. You should also see the file listed beside main.py.

  • Add the customer discovery section to portfolio-brief.md with this content:
# Connected Fleet Risk API POC Brief

## Customer discovery

Customer: Northstar Logistics, a fictional regional fleet operator.

Primary stakeholder: Fleet Safety Manager.

Problem: Vehicle events arrive from multiple telematics sources, but safety teams need one consistent contract that validates incoming data and turns it into an explainable next action.

POC question: Can a vendor-neutral API reject malformed telemetry and return a transparent operational risk assessment that a safety workflow could consume?

Assumptions:

- Telemetry is already available as JSON.
- The scoring rules are illustrative POC rules, not regulatory guidance or a production safety policy.
- Authentication, persistent storage, event streaming, and cloud deployment are outside this local POC.

How Does Discovery Shape the POC?

  • Northstar Logistics gives the demonstration a specific fictional customer.
  • The Fleet Safety Manager identifies the person who needs the operational answer.
  • The POC question turns telemetry validation into a customer-facing test.
  • The assumptions separate the implemented API from future production work.
  • Save portfolio-brief.md.
  • Confirm that the customer problem appears above the assumptions in the editor.

Is the Brief Missing From the File Sidebar?

Confirm that you created portfolio-brief.md inside connected-fleet-risk-api. A similarly named file outside that folder does not become part of this POC.

Check that the filename ends with .md.

Help me locate my portfolio brief.

Map the architecture and success criteria

A solution architecture places the local API inside a broader enterprise workflow. The diagram must clearly distinguish implemented behavior from proposed components.

  • Append this architecture section to portfolio-brief.md after the final assumption:


## Solution architecture

```text
Vehicles and telematics providers
                |
                v
     IoT ingestion or API gateway
                |
                v
     Connected Fleet Risk API
          |                 |
          v                 v
 Safety operations app   Event stream and analytics store
```

The local project implements only the Connected Fleet Risk API. The surrounding components show how the contract could fit into a broader cloud and IoT design.

How Does the Architecture Frame the POC?

  • The telematics sources represent systems that could supply vehicle events.
  • The ingestion layer represents a future entry point for those events.
  • The Connected Fleet Risk API is the only component implemented in this local project.
  • The operations application plus analytics store show possible consumers of the API output.
  • Save portfolio-brief.md.
  • Confirm that the architecture diagram shows the Connected Fleet Risk API between ingestion plus downstream consumers.

Does the Diagram Lose Its Shape?

Confirm that the diagram remains between the opening plus closing ```text fence lines. Those lines preserve the fixed-width spacing.

Compare the vertical bars plus the letter v with the reference block.

Help me fix the architecture diagram.

The optional MyGeotab reference carries unverified pricing. You are not registering for a demo database in this project.

  • Append the success criteria plus optional vendor context to portfolio-brief.md with this content:


## POC success criteria

1. A valid telemetry request returns a score, rating, factors, and recommendation.
2. Negative speed or braking counts produce a validation error before scoring runs.
3. Prepared low, medium, and high scenarios return their expected ratings.
4. A reviewer can discover and execute the endpoint from `/docs` without Postman or a custom frontend.
5. The team can identify clear next steps for authentication, persistence, streaming, observability, and deployment.

## Optional Geotab context

MyGeotab is an optional ecosystem reference, not a dependency of the working POC. Geotab's official setup documentation says demo databases are temporary, contain sample data, and require an expiration date:

https://support.geotab.com/help/mygeotab/access-and-administration/getting-started/database-setup

That setup page does not state a demo-database price. Treat the cost as unverified, review the registration terms, and stop before accepting any charge. The local JSON examples remain the guaranteed project path.

How Do the Criteria Protect the POC?

  • The success criteria convert API behavior into observable evaluation points.
  • The criteria keep authentication plus persistence in the follow-on plan.
  • The optional vendor section records the documented demo conditions.
  • The pricing warning keeps local JSON examples as the guaranteed free path.
  • Save portfolio-brief.md.
  • Confirm that all five success criteria appear before the optional Geotab context.
  • Confirm that the final paragraph tells the reader to stop before accepting any charge.

Is the Scope Boundary Unclear?

Confirm that the brief states that only the Connected Fleet Risk API is implemented. Authentication plus persistence must remain future work.

Keep the production safety-policy limitation in the assumptions. The scoring rules remain illustrative.

Help me review the POC boundaries.

Rehearse the five-minute demo

A demo narrative controls what the reviewer sees during each minute. It starts with discovery before moving to live evidence.

The final minute scores the POC against its criteria. It closes with follow-on work instead of claiming production readiness.

  • Append the five-minute narrative to portfolio-brief.md with this content:


## Five-minute demo narrative

Minute 0 to 1: Frame Northstar Logistics, the safety stakeholder, and the need for a provider-neutral telemetry contract.

Minute 1 to 2: Open `/docs`, show the request schema, and submit malformed telemetry to prove validation happens before scoring.

Minute 2 to 3: Submit the high-risk example and explain the score, rating, contributing factors, and recommended action.

Minute 3 to 4: Walk through the architecture and distinguish the working local API from future cloud and IoT components.

Minute 4 to 5: Score the POC against the success criteria, state the illustrative-rule limitation, and propose authentication, persistence, streaming, observability, and deployment as follow-on work.

How Does the Demo Flow Work?

  • The opening minute establishes the fictional customer problem.
  • The next two minutes demonstrate validation plus risk scoring.
  • The fourth minute explains the boundary around the local API.
  • The final minute uses the success criteria as a scorecard.
  • Save portfolio-brief.md.
  • Confirm that the narrative contains one section for each minute from zero through five.

Is the Demo Story Hard to Follow?

Check that discovery comes before the live request. The architecture should appear after the high-risk response.

Keep production limitations in the final minute. This protects the credibility of the POC.

Help me rehearse the demo story.

✔️ Awesome, I've got everything!

Great. Your saved brief now connects discovery to the working API. It also gives you a timed demonstration plan.

ⓧ I'd like to double check the full code

Compare your complete portfolio-brief.md file with this reference.

# Connected Fleet Risk API POC Brief

## Customer discovery

Customer: Northstar Logistics, a fictional regional fleet operator.

Primary stakeholder: Fleet Safety Manager.

Problem: Vehicle events arrive from multiple telematics sources, but safety teams need one consistent contract that validates incoming data and turns it into an explainable next action.

POC question: Can a vendor-neutral API reject malformed telemetry and return a transparent operational risk assessment that a safety workflow could consume?

Assumptions:

- Telemetry is already available as JSON.
- The scoring rules are illustrative POC rules, not regulatory guidance or a production safety policy.
- Authentication, persistent storage, event streaming, and cloud deployment are outside this local POC.

## Solution architecture

```text
Vehicles and telematics providers
                |
                v
     IoT ingestion or API gateway
                |
                v
     Connected Fleet Risk API
          |                 |
          v                 v
 Safety operations app   Event stream and analytics store
```

The local project implements only the Connected Fleet Risk API. The surrounding components show how the contract could fit into a broader cloud and IoT design.

## POC success criteria

1. A valid telemetry request returns a score, rating, factors, and recommendation.
2. Negative speed or braking counts produce a validation error before scoring runs.
3. Prepared low, medium, and high scenarios return their expected ratings.
4. A reviewer can discover and execute the endpoint from `/docs` without Postman or a custom frontend.
5. The team can identify clear next steps for authentication, persistence, streaming, observability, and deployment.

## Optional Geotab context

MyGeotab is an optional ecosystem reference, not a dependency of the working POC. Geotab's official setup documentation says demo databases are temporary, contain sample data, and require an expiration date:

https://support.geotab.com/help/mygeotab/access-and-administration/getting-started/database-setup

That setup page does not state a demo-database price. Treat the cost as unverified, review the registration terms, and stop before accepting any charge. The local JSON examples remain the guaranteed project path.

## Five-minute demo narrative

Minute 0 to 1: Frame Northstar Logistics, the safety stakeholder, and the need for a provider-neutral telemetry contract.

Minute 1 to 2: Open `/docs`, show the request schema, and submit malformed telemetry to prove validation happens before scoring.

Minute 2 to 3: Submit the high-risk example and explain the score, rating, contributing factors, and recommended action.

Minute 3 to 4: Walk through the architecture and distinguish the working local API from future cloud and IoT components.

Minute 4 to 5: Score the POC against the success criteria, state the illustrative-rule limitation, and propose authentication, persistence, streaming, observability, and deployment as follow-on work.

How to Compare the Full File

Check the section order first. Then compare each paragraph plus list item for missing scope boundaries.

Your OpenAPI documentation provides the live evidence for the story. The high-risk scenario connects the customer problem to an operational response.

Before you submit the request, which parts of the POC story do you expect the response to prove?

  • Switch back to Microsoft Edge at http://127.0.0.1:8000/docs.
  • Return to POST /assess-risk in the documentation from earlier.
  • Keep the vehicle ID from your earlier high-risk request.
  • Set speed_kph to 112.
  • Set harsh_braking_events to 4.
  • Set seatbelt_fastened to false.
  • Submit the request.

You should see risk_score set to 7. You should see risk_rating set to high.

The response should contain three risk factors. Its recommendation should say Escalate for an immediate fleet safety review.

  • Return to portfolio-brief.md in Visual Studio Code.
  • Rehearse the five narrative beats aloud.
  • Keep the rehearsal within five minutes.

You can now move from the Northstar Logistics problem to the live response. You can then explain the architecture plus the success criteria.

That completes the customer-facing layer of your POC. You now have an interview-ready story backed by a working risk assessment.

Secret mission

Assess a Fleet in One Request

Your fleet-risk API can assess multiple vehicles through one batch request. Add a typed list contract that reuses the existing risk engine for every vehicle.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your local POC, pause it for later, or delete it entirely. The project uses no metered cloud resources, so there are no ongoing costs.

No MyGeotab demo database was registered. Your cleanup stays on your Windows computer.

Resources you used:

  • A local FastAPI development server running through fastapi dev.
  • The connected-fleet-risk-api folder containing all project files.
  • The .venv virtual environment containing the installed project dependencies.

Keep everything running

Keep every project resource if you plan to continue improving the POC. Stop the development server between demos to release its local port.

  • Return to the integrated terminal from earlier.
  • Press Ctrl+C to stop the development server.
  • Keep the connected-fleet-risk-api folder.
  • Keep .venv so the installed dependencies remain available.

The terminal returns to a prompt. Your API code, virtual environment, tests, and portfolio brief remain ready for another demo.

Pause - I'll come back to this later

Shut down the running process to release its local port. Your files remain ready for your next session.

  • Return to the integrated terminal from earlier.
  • Press Ctrl+C to stop the development server.
  • Close Visual Studio Code.

Your local port is now free. The complete POC remains inside connected-fleet-risk-api.

When you return, reuse the existing folder and interpreter. This preserves the pinned dependencies from your current setup.

  • Press the Windows key to open system search.
  • Type Visual Studio Code and press Enter.
  • Reopen connected-fleet-risk-api from the recent folders list.
  • Run Python: Select Interpreter from the Command Palette.
  • Choose the .venv interpreter from earlier.
  • Restart the development server by running this command:
fastapi dev

What does this command do?

The development command restarts the local server with automatic reload for the application in main.py.

  • Return to Microsoft Edge.
  • Refresh http://127.0.0.1:8000/docs.

You'll see the interactive API documentation with the single-vehicle and fleet assessment endpoints.

Server Not Starting?

  • Confirm that the .venv interpreter is selected.
  • Help me diagnose why my FastAPI development server will not start.

Delete - I don't want to use this again

Permanent deletion can feel final. It removes only the connected-fleet-risk-api folder while Python and Visual Studio Code stay installed.

  • Return to the integrated terminal from earlier.
  • Press Ctrl+C to stop the development server.

The terminal returns to a prompt. This confirms that the local server has stopped.

  • Close Visual Studio Code.
  • Press the Windows key to open system search.
  • Search for the Windows file browser.
  • Open the matching file browser result.
  • Return to the parent folder where you created connected-fleet-risk-api.
  • Select the connected-fleet-risk-api folder.
  • Use the file browser's delete action to remove the selected folder.
  • Approve the deletion prompt.

The connected-fleet-risk-api folder no longer appears in the file browser. This removes main.py, portfolio-brief.md, requirements.txt, and .venv.

Nice Work!

Nice Work!

You did it! Your local FastAPI service now turns validated fleet telemetry into an explainable risk assessment. Your portfolio brief gives that working POC a customer-facing story.

You've learned how to:

  • Build a local fleet-risk API with interactive OpenAPI documentation at http://127.0.0.1:8000/docs. Use one browser page to demonstrate valid telemetry plus the original malformed-data limitation.
  • Replace the raw dictionary with a constrained Pydantic API contract. Reject malformed telemetry before calculate_risk() runs. Return a structured RiskAssessment with an explainable operational decision.
  • Produce a customer-facing POC story for Northstar Logistics. Ground the narrative in discovery. Map the vendor-neutral architecture. Measure the POC against clear success criteria. Deliver the complete flow as a five-minute demo.
  • Secret Mission: Add a typed batch endpoint at POST /assess-fleet. Reuse calculate_risk() for every vehicle. Preserve the input order in the response.

Ready to quiz yourself?