openapi: "3.1.0"
info:
  title: Nuuk Provider API
  version: "1.0.0"
  description: |
    Partner-facing telematics and camera API for third-party integrations (e.g. Terminal).

    Authenticate with a company API key (`X-API-KEY`). Keys use the INTEGRATION role
    and are scoped to one company (US DOT). Resource IDs are plain numeric strings.

    ## Camera media capabilities
    1. **List cameras** ? `GET /nuuk/v1/devices`
    2. **List safety events** ? `GET /nuuk/v1/safety/events` (**coming soon** ? not partner-ready)
    3. **On-demand clips** ? `POST /nuuk/v1/cameras/{deviceId}/clips` then poll `GET .../clips/{requestId}`
       (device must be online; offline returns 400)
    4. **Event camera media** ? `GET /nuuk/v1/safety/events/{id}/camera-media`
       (**coming soon** with safety events; read-only / idempotent when events exist)
  contact:
    name: Nuuk API Support
    email: support@withnuuk.com
servers:
  - url: https://api.nuuk.com
    description: Production
  - url: https://playground.api.nuuk.com
    description: Sandbox
tags:
  - name: Vehicles
    description: Vehicle identity
  - name: Drivers
    description: Driver identity
  - name: Devices
    description: Camera devices
  - name: Safety
    description: |
      Safety events and related camera media.

      **Coming soon (not partner-ready).** Routes stay in the contract for stability.
      Do not build production adapters against them yet.
  - name: Clips
    description: On-demand camera clips

security:
  - apiKeyAuth: []

paths:
  /nuuk/v1/vehicles:
    get:
      tags: [Vehicles]
      summary: List vehicles
      operationId: listProviderVehicles
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/Vehicle"
                  next:
                    type: string
                    nullable: true
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /nuuk/v1/vehicles/{vehicleId}:
    get:
      tags: [Vehicles]
      summary: Get vehicle
      operationId: getProviderVehicle
      parameters:
        - $ref: "#/components/parameters/VehicleId"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Vehicle"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /nuuk/v1/drivers:
    get:
      tags: [Drivers]
      summary: List drivers
      operationId: listProviderDrivers
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/Driver"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /nuuk/v1/drivers/{driverId}:
    get:
      tags: [Drivers]
      summary: Get driver
      operationId: getProviderDriver
      parameters:
        - name: driverId
          in: path
          required: true
          schema:
            type: string
            example: "10"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Driver"
        "404":
          $ref: "#/components/responses/NotFound"

  /nuuk/v1/devices:
    get:
      tags: [Devices]
      summary: List cameras
      description: |
        Returns camera devices for the connected company.
        `type` is always `camera`. `sourceId` is the device serial / IMEI.
        `vehicle` is set when the device is linked to a company vehicle.
        `lastConnectedAt` is device last-contact when the connectivity service is reachable;
        otherwise falls back to the camera registry `updatedAt`.
      operationId: listProviderDevices
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/Device"
              example:
                results:
                  - id: "1"
                    sourceId: "860123456789012"
                    provider: nuuk
                    vehicle: "42"
                    type: camera
                    description: AW300
                    serialNumber: "860123456789012"
                    model: AW300
                    lastConnectedAt: "2026-09-03T18:00:00Z"

  /nuuk/v1/safety/events:
    get:
      tags: [Safety]
      summary: List safety events (coming soon)
      description: |
        **Coming soon ? not partner-ready.** Keep this route for contract stability; do not
        build production adapters against it yet.
      operationId: listProviderSafetyEvents
      parameters:
        - name: vehicleIds
          in: query
          required: false
          description: Comma-separated vehicle IDs (`1,2`)
          schema:
            type: string
        - name: startedAfter
          in: query
          schema:
            type: string
            format: date-time
        - name: startedBefore
          in: query
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      $ref: "#/components/schemas/SafetyEvent"

  /nuuk/v1/safety/events/{eventId}:
    get:
      tags: [Safety]
      summary: Get safety event (coming soon)
      description: |
        **Coming soon ? not partner-ready.**
      operationId: getProviderSafetyEvent
      parameters:
        - name: eventId
          in: path
          required: true
          schema:
            type: string
            example: "1001"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SafetyEvent"
        "404":
          $ref: "#/components/responses/NotFound"

  /nuuk/v1/safety/events/{eventId}/camera-media:
    get:
      tags: [Safety]
      summary: Get event camera media (coming soon)
      description: |
        **Coming soon with safety events ? not partner-ready.**

        For on-demand footage, use `POST /cameras/{deviceId}/clips` (that path remains available).
      operationId: getProviderEventCameraMedia
      parameters:
        - name: eventId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CameraMedia"
              example:
                frontFacing:
                  sourceId: "55"
                  videoUrl: "https://storage.example/signed-url"
                rearFacing: null

  /nuuk/v1/cameras/{deviceId}/clips:
    post:
      tags: [Clips]
      summary: Request on-demand clip
      description: |
        Requests an on-demand video clip from the camera device.
        Duration is rounded up to whole minutes (1?10).
        Returns HTTP 400 if the camera device is offline.
      operationId: requestProviderClip
      parameters:
        - name: deviceId
          in: path
          required: true
          schema:
            type: string
            example: "1"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClipRequest"
            example:
              startedAt: "2026-09-03T17:55:00Z"
              durationSeconds: 60
              cameraFacing: front
      responses:
        "200":
          description: Clip request accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Clip"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"

  /nuuk/v1/cameras/{deviceId}/clips/{requestId}:
    get:
      tags: [Clips]
      summary: Get clip status / URL
      operationId: getProviderClip
      parameters:
        - name: deviceId
          in: path
          required: true
          schema:
            type: string
        - name: requestId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Clip"
        "404":
          $ref: "#/components/responses/NotFound"

components:
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY

  parameters:
    VehicleId:
      name: vehicleId
      in: path
      required: true
      schema:
        type: string
        example: "42"

  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

  schemas:
    Error:
      type: object
      properties:
        code:
          type: string
          enum: [bad_request, unauthorized, forbidden, not_found, internal_error]
        message:
          type: string
        detail:
          type: string

    Vehicle:
      type: object
      properties:
        id:
          type: string
          example: "42"
        sourceId:
          type: string
        provider:
          type: string
          example: nuuk
        status:
          type: string
          enum: [active, inactive]
        vin:
          type: string
        name:
          type: string
        make:
          type: string
        model:
          type: string
        year:
          type: integer
        licensePlate:
          type: object
          properties:
            state:
              type: string
            number:
              type: string

    Driver:
      type: object
      properties:
        id:
          type: string
          example: "10"
        sourceId:
          type: string
        provider:
          type: string
          example: nuuk
        status:
          type: string
        firstName:
          type: string
        lastName:
          type: string
        email:
          type: string
        phone:
          type: string
        license:
          type: object
          properties:
            state:
              type: string
            number:
              type: string

    Device:
      type: object
      properties:
        id:
          type: string
          example: "1"
        sourceId:
          type: string
          description: Device IMEI
        provider:
          type: string
          example: nuuk
        vehicle:
          type: string
          description: Linked vehicle ID when VIN matches
        type:
          type: string
          enum: [camera]
        description:
          type: string
        serialNumber:
          type: string
        model:
          type: string
        lastConnectedAt:
          type: string
          format: date-time
          description: |
            Device last-contact from the connectivity service when available;
            otherwise camera registry updatedAt.

    SafetyEvent:
      type: object
      properties:
        id:
          type: string
          example: "1001"
        sourceId:
          type: string
        provider:
          type: string
        type:
          type: string
          description: Coming soon. Event types are reserved until this capability ships.
        vehicle:
          type: string
        driver:
          type: string
        startedAt:
          type: string
          format: date-time
        startLocation:
          type: object
          properties:
            latitude:
              type: number
            longitude:
              type: number
        stats:
          type: object
          properties:
            maximumSpeed:
              type: number
            gForce:
              type: number
        cameraMedia:
          type: object
          properties:
            frontFacing:
              $ref: "#/components/schemas/CameraMediaFileReference"
            rearFacing:
              $ref: "#/components/schemas/CameraMediaFileReference"

    CameraMediaFileReference:
      type: object
      properties:
        available:
          type: boolean
        sourceId:
          type: string

    CameraMedia:
      type: object
      properties:
        frontFacing:
          $ref: "#/components/schemas/CameraMediaFile"
        rearFacing:
          $ref: "#/components/schemas/CameraMediaFile"

    CameraMediaFile:
      type: object
      properties:
        sourceId:
          type: string
        videoUrl:
          type: string
        imageUrl:
          type: string

    ClipRequest:
      type: object
      required: [startedAt, durationSeconds]
      properties:
        startedAt:
          type: string
          format: date-time
        durationSeconds:
          type: integer
          minimum: 1
          maximum: 600
        cameraFacing:
          type: string
          enum: [front, rear]
          default: front

    Clip:
      type: object
      properties:
        id:
          type: string
        deviceId:
          type: string
        status:
          type: string
          description: requested, uploading, combining, available, failed
        cameraFacing:
          type: string
        startedAt:
          type: string
          format: date-time
        durationSeconds:
          type: integer
        videoUrl:
          type: string
        imageUrl:
          type: string
        errorMessage:
          type: string
