intermediate
permit
conditions
compliance

Permit-Condition Reports

How permit_conditions rows drive the daily compliance scoreboard — fleet cap, equity-zone deployment, complaint SLA, EOD trip report, and corral utilization.

Levy Fleets TeamMay 18, 202613 min read

Permit-Condition Reports

Most city permits include enforceable conditions — fleet cap, equity-zone deployment percentage, complaint SLA, end-of-day trip report, parking corral utilization. Levy stores each as a row in permit_conditions, evaluates them on a schedule, and renders the pass/fail trend in both the operator dashboard and the city portal.

The five condition types

condition_typeWhat it checksCadence
fleet_capCOUNT(active vehicles in jurisdiction) <= capDaily
equity_zone_pct% of deployed fleet inside equity geographies >= thresholdHourly
complaint_slaEach complaint's responded_at within N hours of created_atDaily
trip_report_eodA trip CSV is generated daily at 23:59 jurisdiction-local timeDaily
corral_utilizationHourly snapshot of parking corral occupancyHourly

Each row in permit_conditions carries the jurisdiction_id, condition_type, and a config JSONB that varies by type.

Configuring a condition

From the operator dashboard at /dashboard/compliance/{jurisdiction-id} -> Permit conditions tab, click Add condition.

fleet_cap

{
  "cap": 250,
  "vehicle_types": ["scooter"],
  "exclude_statuses": ["maintenance", "removed"]
}

The evaluator counts vehicles where:

  • subaccount_id matches the jurisdiction's subaccount
  • last_lat/last_lng is inside mds_jurisdictions.geometry (or NULL with last_seen in the last 24h — generous to vehicles that just lost GPS)
  • status NOT IN config.exclude_statuses
  • model.vehicle_type IN config.vehicle_types (if set)

Pass = count <= cap. Fail = count > cap. The dashboard scoreboard shows the current count and the cap side by side.

equity_zone_pct

{
  "threshold_pct": 20,
  "equity_geography_ids": ["geo-1", "geo-2"],
  "min_vehicles_required": 5
}

Pass = deployed-in-equity / total-deployed >= threshold_pct / 100. The min_vehicles_required is a floor — if there are fewer than that deployed at all, the condition is marked n/a for the hour rather than failed (sparse early-morning deployments).

Equity geographies come from either the city's Policy feed (rules with rule_type = 'equity_zone') or a manually configured list (when the city doesn't publish them — common with smaller cities). The manual list lives in the same permit_conditions.config.

complaint_sla

{
  "max_response_hours": 24,
  "complaint_sources": ["311", "in-app", "email"]
}

The evaluator joins complaints to find rows where responded_at IS NULL AND created_at < now() - interval '<hours>' (open and overdue) or responded_at - created_at > interval '<hours>' (closed but late). Pass = zero late complaints in the window.

trip_report_eod

{
  "format": "csv",
  "delivery": "email",
  "recipient_email": "permits@city.gov"
}

A scheduled report runs at 23:59 jurisdiction-local time and emits a trip CSV for the day. Pass = the report was successfully delivered. Fail = email bounced or the report row didn't materialize.

corral_utilization

{
  "min_utilization_pct": 10,
  "max_utilization_pct": 90,
  "exclude_corral_ids": []
}

Hourly snapshot of each parking corral's occupancy. Pass = all corrals between the configured floor and ceiling. Fail = at least one corral outside the band. The condition is used by cities that want to ensure parking is actually being used (low end) and that operators aren't over-concentrating (high end).

The scoreboard

Operators see the scoreboard at /dashboard/compliance/{jurisdiction-id}. Cities see it inside the city portal at /city/{slug}. The shape is the same:

ConditionCurrent valueThresholdStatus (today)30-day pass rate
Fleet cap247 vehicles250Pass100%
Equity zone %18%>= 20%Fail88%
Complaint SLA2 open over 24h0Fail92%
Trip report EODDeliveredn/aPass100%
Corral utilization6 of 6 in bandAll in bandPass95%

Failing conditions are highlighted in red. The 30-day pass rate is computed from the city_compliance_reports history.

The reporter library

src/lib/compliance/permit-reporter.ts exposes per-condition evaluators. Each takes the jurisdiction + the relevant permit_conditions.config and returns:

type ConditionResult = {
  condition_id: string;
  passed: boolean;
  current_value: number | string;
  threshold: number | string;
  measured_at: string; // RFC3339
  details: Record<string, unknown>;
};

The aggregate report at digest time:

const report = await buildComplianceReport({
  jurisdictionId,
  period: 'daily', // or 'weekly' / 'monthly'
  date: '2026-05-18',
});

// report.conditions: ConditionResult[]
// report.summary.passed: number
// report.summary.failed: number
// report.trips: { count, total_distance_km, ... }
// report.complaints: { open, closed, late, ... }
// report.enforcement_events: { speed_limits_applied, locks_issued, ... }

This shape is what the digest email renders, what the compliance-report API serves, and what gets persisted to city_compliance_reports.payload.

The compliance-report API

City contacts pull formal reports from:

GET /api/city/{slug}/compliance-report?period=monthly&date=2026-05

Returns the same JSON shape as the digest plus a pdf_url when period=monthly (we render a one-page PDF for monthly reports — useful for permit renewals).

Every fetched report is persisted to city_compliance_reports — the audit trail of every report the city has actually retrieved. If a city auditor asks "did you send a report for January?", the row in city_compliance_reports is the answer.

When conditions fail

A failing condition does not auto-pause your fleet. It surfaces:

  • A red row on the scoreboard
  • A line in today's digest email
  • A Sentry breadcrumb tagged compliance.condition.failed

What you do about it depends on the condition. For fleet_cap overage, deploy fewer vehicles. For equity_zone_pct, re-distribute. For complaint_sla, respond to the open complaints. The system doesn't make operational decisions for you.

The exception is fleet_cap, where the dashboard offers a "Pause new ride starts in jurisdiction" toggle. This is a soft constraint — existing rides continue, no new rides start in the jurisdiction until the cap is back under threshold. The toggle is operator-controlled; we don't activate it automatically because false-positive auto-pauses would be much worse than a transient fleet_cap fail.

Custom conditions

If your permit includes a condition that doesn't fit the five built-in types (e.g., "must achieve 90% multilingual signage"), add a row with condition_type = 'custom' and a manual passed field. The evaluator returns whatever you set; cities still see it on the scoreboard. This is the escape hatch for permits that have one weird clause.

What's next

Running a fleet on this hardware?

Levy Fleets runs scooters, e-bikes, mopeds and golf carts on the IoT hardware you already have, with integration pages for 29 vendors including Segway, Okai, Omni, Queclink, Invers and Comodule. Keyless unlock, geofencing, rider payments and rider support are included, and the Managed plan is a share of revenue with no per-vehicle software fee.