intermediate
getting-started
jurisdictions
onboarding

Getting Started with Levy Compliance

End-to-end onboarding for a new city jurisdiction — from permit award to first compliance digest emailed to the city.

Levy Fleets TeamMay 18, 202614 min read

Getting Started with Levy Compliance

This article walks you through onboarding a single jurisdiction (one city) end-to-end. The end state: MDS endpoints are live, the city's Policy feed is being polled every 60 seconds, geofences activate on schedule, and the city contact receives daily/weekly/monthly digest emails.

Prerequisites

Before you begin, confirm the following:

RequirementWhere to check
You hold an active operator permit (or are in the application phase)City permit office
Your fleet has at least one vehicle with iot_imei set/dashboard/vehicles
Your subaccount has an MDS bearer token issuedSettings -> API & Integrations
You have the city's Policy feed URL (and auth token, if private)City contact
You have at least one city contact email for digest deliveryCity contact

MDS bearer tokens

Levy Compliance still uses the existing bearer-token table (mds_city_tokens) for primary auth. The new JWT layer is signed on top of every response — cities can either continue using bearer tokens or verify the JWT against your JWKS URL. See JWKS Key Management.

Step 1 — Create the jurisdiction

1

Open the compliance dashboard

Go to Dashboard -> Compliance at /dashboard/compliance. You will see a jurisdictions index (empty if this is your first one) plus a conflict alert banner at the top.

2

Click 'Add jurisdiction'

Navigates to /dashboard/compliance/new. The form fields map 1:1 to the mds_jurisdictions table.

3

Fill in the form

FieldExampleNotes
NameBoulder, CODisplay name shown to operators and cities.
Slugboulder-coUsed in the public URL: /city/boulder-co. Lowercase, dash-separated, must be unique.
Policy feed URLhttps://populus.cityofboulder.org/mds/policyThe city's published Policy endpoint.
Policy feed auth tokenbearer xyz...Optional. Stored encrypted.
Poll interval (s)60Default 60s. Cities with high update churn can go lower; quiet cities can go higher.
ActivetrueDisable to pause polling without deleting.
4

Save

The jurisdiction is created. The first JWKS keypair is lazy-minted the next time anyone hits /api/mds/{subaccountId}/.well-known/jwks.json — you can trigger it now by opening that URL in a browser.

Step 2 — Confirm MDS endpoints respond

Each jurisdiction inherits the same MDS endpoint base under your subaccount:

GET /api/mds/{subaccountId}/provider/v2/vehicles
GET /api/mds/{subaccountId}/provider/v2/vehicles/status
GET /api/mds/{subaccountId}/provider/v2/trips
GET /api/mds/{subaccountId}/provider/v2/events
GET /api/mds/{subaccountId}/provider/v2/telemetry/{vehicle_id}
GET /api/mds/{subaccountId}/provider/v2/stops
GET /api/mds/{subaccountId}/provider/v2/reports
GET /api/mds/{subaccountId}/.well-known/jwks.json

Hit vehicles/status with your bearer token to confirm:

curl -H "Authorization: Bearer <YOUR_MDS_TOKEN>" \
  https://fleets.levyelectric.com/api/mds/<subaccountId>/provider/v2/vehicles/status

A successful response carries:

  • HTTP 200
  • Content-Type: application/vnd.mds+json;version=2.0
  • MDS-JWT header (signed proof of the response body sha256)
  • A version field set to 2.0.1 in the JSON envelope

See MDS Provider Setup for the full endpoint reference.

Step 3 — Hand the URLs to the city

Send the city contact a short email with these four pieces:

ItemValue
MDS Provider basehttps://fleets.levyelectric.com/api/mds/<subaccountId>/provider/v2/
JWKS URLhttps://fleets.levyelectric.com/api/mds/<subaccountId>/.well-known/jwks.json
Bearer token(issue from Settings -> API & Integrations)
GBFS 3.0 roothttps://fleets.levyelectric.com/api/gbfs/v3/<subaccountId>/gbfs.json

The city's Populus / Ride Report / Lacuna instance will validate the endpoints against the MobilityData GBFS validator and the OMF mds-provider-validator before flagging your fleet as live on their dashboard. Both validators should pass with no warnings.

Step 4 — Watch the first Policy poll

The mds-policy-poll cron runs every minute. Within 60 seconds of saving the jurisdiction:

  1. The poller hits the city's Policy feed.
  2. The raw payload is sha256-hashed and compared to the last-known hash.
  3. If it changed, the Zod validator parses policies[] and geographies[].
  4. Policies are upserted; rules are replaced wholesale; policy_geofences rows are materialized from the referenced geographies.
  5. A row lands in mds_policy_audit with run_id, diff JSON, and applied_at.

Open /dashboard/compliance/{jurisdiction-id} and you should see the active policies list populated. See Policy Ingestion from Cities for the diff viewer.

Don't see any policies?

Three common causes: (1) the city's feed URL is wrong or behind auth you didn't configure, (2) the feed validates but contains no policies for your fleet's vehicle types, or (3) the feed responded but its schema doesn't match MDS 2.0. The audit log records the parse error; check there first.

Step 5 — Wire up city contacts

City contacts get magic-link access to the City Portal and receive digest emails on a cadence you set.

1

From the jurisdiction page, click 'Add city contact'

Adds a row to city_contacts scoped to this jurisdiction.

2

Set name, email, role, and digest_frequency

FieldExample
Emailsarah@bouldercolorado.gov
NameSarah Johnson
RoleCity Compliance Officer
Portal accesstrue
Digest frequencyweekly (options: daily, weekly, monthly)
3

Save and notify the contact

The contact can now go to /city/{slug}, enter their email, and receive a magic link. They will also start receiving digest emails on the chosen cadence.

Step 6 — Define permit conditions

If your permit specifies enforceable conditions (fleet cap, equity-zone deployment percentage, complaint SLA), record them in permit_conditions. The compliance reporter evaluates them on each digest run.

condition_typeWhat it checks
fleet_capCOUNT(active vehicles in jurisdiction) &lt;= cap
equity_zone_pct% of deployed fleet inside equity geographies &gt;= threshold
complaint_slaEach complaint's responded_at within N hours of created_at
trip_report_eodA trip CSV is generated daily at 23:59 jurisdiction-local time
corral_utilizationHourly snapshot of parking corral occupancy

See Permit-Condition Reports for the evaluator details.

Step 7 — Verify the first digest

The compliance-digest cron runs hourly and walks every city_contacts row whose cadence window has elapsed. For a daily contact created today, the first email lands within an hour after the digest period closes (typically midnight in the jurisdiction's local time).

When the email goes out:

  • A row is written to city_compliance_reports with payload (the rendered JSON) and pdf_url if a monthly PDF was attached.
  • The contact's last_digest_sent_at is updated.

If the city doesn't see the email after the expected window, check spam, then check last_digest_sent_at in city_contacts — if it's stale, the cron didn't fire or the sender errored. See Troubleshooting.

What you've accomplished

After steps 1-7:

  • Your subaccount publishes a conformant MDS 2.0 Provider feed
  • A GBFS 3.0 feed (with vehicle_type_id per zone rule) is live alongside the legacy 2.x feed
  • The city's Policy feed is polled every minute and diffed against last-known
  • Stacked geofences with the correct priority materialize from city rules
  • The city contact can log in to the portal via magic link
  • Digest emails will flow on the configured cadence
  • Permit-condition pass/fail is computed daily and shown in both the operator dashboard and city portal

You can now point your city permit officer at the public MDS endpoints and the GBFS 3.0 root, and let validators do their thing.

What's next