- contracts/openapi.yaml: title is now "Fleet Ops API"; documents the new status-recommendation preview endpoint and the apply endpoint's request body (recommendation_token) and full error-code set; notes the search endpoint's code+params response shape. - README.md: title and intro now say Fleet Ops, with an explicit note on the Fleet Ops (visible)/MobilityOps (technical identifier) naming split; refreshed stale test counts (151 backend, 108 Playwright). - PROJECT_STATE.md: full progress record for the in-progress correction milestone, including what's done, what bugs were found and fixed, and what's explicitly not yet done (i18n test-strengthening 11D/E/F, clean-checkout drill, Unraid deployment, merge to master). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
391 lines
13 KiB
YAML
391 lines
13 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Fleet Ops API
|
|
version: 0.1.0
|
|
description: >-
|
|
Contract baseline for the Fleet Ops demo. "Fleet Ops" is the visible product name;
|
|
"mobilityops" remains the technical identifier for the repository, deployment
|
|
directory, database, and internal service/health identifiers only.
|
|
servers:
|
|
- url: http://localhost:8128
|
|
paths:
|
|
/health:
|
|
get:
|
|
operationId: health
|
|
responses:
|
|
'200':
|
|
description: Healthy
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Health'
|
|
/api/v1/dashboard:
|
|
get:
|
|
operationId: getDashboard
|
|
responses:
|
|
'200':
|
|
description: Persisted operational summary
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Dashboard'
|
|
/api/v1/vehicles:
|
|
get:
|
|
operationId: listVehicles
|
|
parameters:
|
|
- in: query
|
|
name: status
|
|
schema:
|
|
type: string
|
|
- in: query
|
|
name: attention_only
|
|
schema:
|
|
type: boolean
|
|
default: false
|
|
responses:
|
|
'200':
|
|
description: Vehicle list
|
|
/api/v1/vehicles/{public_ref}:
|
|
get:
|
|
operationId: getVehicle
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
responses:
|
|
'200':
|
|
description: Vehicle detail
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
/api/v1/bookings:
|
|
get:
|
|
operationId: listBookings
|
|
responses:
|
|
'200':
|
|
description: Booking list
|
|
/api/v1/bookings/{public_ref}:
|
|
get:
|
|
operationId: getBooking
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
responses:
|
|
'200':
|
|
description: Booking detail
|
|
/api/v1/bookings/{public_ref}/return-preview:
|
|
post:
|
|
operationId: previewVehicleReturn
|
|
description: >-
|
|
Non-mutating evaluation of what committing this return would do. Shares its
|
|
domain evaluation with the commit endpoint below so the two can never drift.
|
|
No writes, no audit event, no outbox event.
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterReturnRequest'
|
|
responses:
|
|
'200':
|
|
description: Authoritative evaluation of the resulting fleet state
|
|
'409':
|
|
description: Booking is not active
|
|
/api/v1/bookings/{public_ref}/return:
|
|
post:
|
|
operationId: registerVehicleReturn
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
- in: header
|
|
name: Idempotency-Key
|
|
required: true
|
|
schema:
|
|
type: string
|
|
minLength: 8
|
|
maxLength: 128
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterReturnRequest'
|
|
responses:
|
|
'200':
|
|
description: Existing idempotent result replayed
|
|
'201':
|
|
description: Return registered
|
|
'409':
|
|
description: State or concurrency conflict
|
|
/api/v1/data-quality/issues:
|
|
get:
|
|
operationId: listDataQualityIssues
|
|
description: Operations Manager only.
|
|
responses:
|
|
'200':
|
|
description: Quality issues
|
|
/api/v1/data-quality/scan:
|
|
post:
|
|
operationId: runDataQualityScan
|
|
description: Manual trigger for the deterministic five-rule scan. Operations Manager only.
|
|
responses:
|
|
'200':
|
|
description: Counts of newly created issues per rule type
|
|
/api/v1/data-quality/issues/{public_ref}/merge-customers:
|
|
post:
|
|
operationId: mergeDuplicateCustomers
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
responses:
|
|
'200':
|
|
description: Merge completed
|
|
'409':
|
|
description: Issue no longer mergeable
|
|
/api/v1/data-quality/issues/{public_ref}/provide-fields:
|
|
post:
|
|
operationId: provideMissingFields
|
|
description: >-
|
|
missing_required_field only. Resolves once nothing required remains missing;
|
|
otherwise leaves the issue open with updated evidence.
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
responses:
|
|
'200':
|
|
description: Issue after the update (may still be open)
|
|
'409':
|
|
description: Wrong rule type or issue not open
|
|
'422':
|
|
description: Disallowed field or empty value
|
|
/api/v1/data-quality/issues/{public_ref}/resolve-odometer-regression:
|
|
post:
|
|
operationId: resolveOdometerRegression
|
|
description: >-
|
|
odometer_regression only. Either retains the canonical odometer, or corrects a
|
|
related booking's reading -- a correction below the current canonical value is
|
|
rejected, since it would not resolve the regression.
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
responses:
|
|
'200':
|
|
description: Issue resolved
|
|
'409':
|
|
description: Wrong rule type or issue not open
|
|
'422':
|
|
description: Invalid booking reference or a correction below canonical
|
|
/api/v1/data-quality/issues/{public_ref}/resolve-overlap:
|
|
post:
|
|
operationId: resolveBookingOverlap
|
|
description: >-
|
|
booking_overlap only. Blocks one of the two overlapping bookings and
|
|
re-verifies no overlap remains before resolving.
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
responses:
|
|
'200':
|
|
description: Issue resolved
|
|
'409':
|
|
description: Wrong rule type, issue not open, or overlap still present
|
|
'422':
|
|
description: booking_ref not one of the overlapping bookings
|
|
/api/v1/data-quality/issues/{public_ref}/status-recommendation:
|
|
post:
|
|
operationId: previewVehicleStatusRecommendation
|
|
description: >-
|
|
vehicle_status_conflict only. Non-mutating: computes the recommendation from
|
|
the same shared evaluator the scanner and apply endpoint use
|
|
(app.services.vehicle_status.evaluate_vehicle_status), without resolving the
|
|
issue, writing an audit event, or queuing automation. Safe to call repeatedly
|
|
-- see docs/fleet-ops-correction/vehicle-status-decision-table.md.
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
responses:
|
|
'200':
|
|
description: >-
|
|
Current/recommended status, recommendation code, safe_to_apply,
|
|
manual_review_required, the underlying facts, and a recommendation_token
|
|
the apply endpoint revalidates against.
|
|
'409':
|
|
description: Wrong rule type, issue not open, or vehicle not found
|
|
/api/v1/data-quality/issues/{public_ref}/apply-recommended-status:
|
|
post:
|
|
operationId: applyRecommendedVehicleStatus
|
|
description: >-
|
|
vehicle_status_conflict only. Applies the one authoritative recommendation
|
|
function's output within one transaction: locks the issue and vehicle,
|
|
recomputes the recommendation from fresh facts, rejects the request if the
|
|
supplied recommendation_token no longer matches (RECOMMENDATION_STALE), refuses
|
|
an unsafe/manual-review recommendation (MANUAL_REVIEW_REQUIRED) or a
|
|
recommendation with nothing to apply (NO_CONFLICT_DETECTED), then re-validates
|
|
the same evaluator post-write before resolving the issue.
|
|
parameters:
|
|
- $ref: '#/components/parameters/PublicRef'
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [recommendation_token]
|
|
properties:
|
|
recommendation_token:
|
|
type: string
|
|
description: The token from the most recent status-recommendation preview call.
|
|
responses:
|
|
'200':
|
|
description: Applied status, reason code and the resolved issue
|
|
'409':
|
|
description: >-
|
|
Wrong rule type, issue not open, vehicle not found, stale recommendation
|
|
token, manual review required, no conflict detected, or the applied status
|
|
did not resolve the conflict on re-validation
|
|
/api/v1/search:
|
|
get:
|
|
operationId: search
|
|
description: >-
|
|
Bounded typed results (vehicle, booking, data_quality_issue, section).
|
|
Data-quality and manager-only sections are filtered server-side by role.
|
|
Customers are never returned -- no customer detail route exists. Every result's
|
|
`label` is a stable public_ref/section id (never translatable prose); `detail_code`
|
|
(+ optional `detail_params` for data values like make/model/location) is what the
|
|
frontend localizes -- the backend never emits English/Dutch/French sentences here.
|
|
parameters:
|
|
- in: query
|
|
name: q
|
|
required: true
|
|
schema:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 100
|
|
responses:
|
|
'200':
|
|
description: Search results
|
|
/api/v1/integrations/status:
|
|
get:
|
|
operationId: getIntegrationStatus
|
|
description: >-
|
|
Truthful aggregate n8n state derived from outbox delivery counts (not just the
|
|
most recent event), plus the actual MCP Hub registration_enabled setting.
|
|
Operations Manager only.
|
|
responses:
|
|
'200':
|
|
description: n8n and MCP Hub integration status
|
|
/api/v1/integrations/n8n/scheduled-scan:
|
|
post:
|
|
operationId: n8nScheduledScan
|
|
description: >-
|
|
Triggered by the scheduled n8n quality-scan workflow. Runs the same run_scan()
|
|
the manual UI action uses; idempotent by construction.
|
|
security:
|
|
- serviceToken: []
|
|
responses:
|
|
'200':
|
|
description: Counts of newly created issues per rule type
|
|
'401':
|
|
description: Invalid service token
|
|
/api/v1/knowledge/questions:
|
|
post:
|
|
operationId: askKnowledgeQuestion
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [question]
|
|
properties:
|
|
question:
|
|
type: string
|
|
minLength: 3
|
|
maxLength: 1000
|
|
responses:
|
|
'200':
|
|
description: Grounded, insufficient or unavailable answer
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/GroundedAnswer'
|
|
/api/v1/integrations/mcp/operations-summary:
|
|
get:
|
|
operationId: mcpOperationsSummary
|
|
security:
|
|
- serviceToken: []
|
|
responses:
|
|
'200':
|
|
description: Read-only operational summary
|
|
components:
|
|
securitySchemes:
|
|
serviceToken:
|
|
type: http
|
|
scheme: bearer
|
|
parameters:
|
|
PublicRef:
|
|
in: path
|
|
name: public_ref
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
NotFound:
|
|
description: Not found
|
|
schemas:
|
|
Health:
|
|
type: object
|
|
required: [status, service]
|
|
properties:
|
|
status:
|
|
const: ok
|
|
service:
|
|
const: mobilityops-api
|
|
Dashboard:
|
|
type: object
|
|
required: [metrics, attention_items]
|
|
properties:
|
|
metrics:
|
|
type: object
|
|
additionalProperties:
|
|
type: integer
|
|
attention_items:
|
|
type: array
|
|
items:
|
|
type: object
|
|
RegisterReturnRequest:
|
|
type: object
|
|
required: [end_odometer_km, fuel_level_percent, cleanliness_ok, damage_reported, technical_warning]
|
|
properties:
|
|
end_odometer_km:
|
|
type: integer
|
|
minimum: 0
|
|
fuel_level_percent:
|
|
type: integer
|
|
minimum: 0
|
|
maximum: 100
|
|
cleanliness_ok:
|
|
type: boolean
|
|
damage_reported:
|
|
type: boolean
|
|
technical_warning:
|
|
type: boolean
|
|
notes:
|
|
type: string
|
|
maxLength: 2000
|
|
GroundedAnswer:
|
|
type: object
|
|
required: [answer, evidence_state, sources, provider, correlation_id]
|
|
properties:
|
|
answer:
|
|
type: string
|
|
evidence_state:
|
|
enum: [grounded, insufficient, unavailable]
|
|
sources:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required: [document_id, title, version, section, excerpt]
|
|
properties:
|
|
document_id: {type: string}
|
|
title: {type: string}
|
|
version: {type: string}
|
|
section: {type: string}
|
|
excerpt: {type: string}
|
|
provider:
|
|
type: string
|
|
correlation_id:
|
|
type: string
|