Files
MobilityOps/contracts/openapi.yaml
T
NuklearRabbitandClaude Sonnet 5 a7ac5ed9d0 docs: update OpenAPI contract, README branding, and PROJECT_STATE for the correction milestone
- 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>
2026-08-03 23:14:34 +02:00

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