openapi: 3.1.0
info:
  title: MASAR Partner API
  version: 1.0.0
  description: Read-only integration API for authorized MASAR transport organizations.
  termsOfService: https://dev.masardz.com/terms.html
  contact:
    name: MASAR Developer Support
    url: https://dev.masardz.com/support.html
servers:
- url: https://api.masardz.com/api/v1/partner
  description: Production
security:
- BearerApiKey: []
components:
  securitySchemes:
    BearerApiKey:
      type: http
      scheme: bearer
      bearerFormat: MASAR_API_KEY
  parameters:
    Page:
      name: page
      in: query
      schema:
        type: integer
        minimum: 1
        default: 1
    PerPage:
      name: per_page
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
  schemas:
    Meta:
      type: object
      properties:
        request_id:
          type: string
        scope:
          type:
          - string
          - 'null'
        rate_limit:
          type: object
          properties:
            limit:
              type: integer
            remaining:
              type: integer
            reset_at:
              type: integer
    Pagination:
      type: object
      properties:
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        total_pages:
          type: integer
    Bus:
      type: object
      properties:
        id:
          type: integer
        plate_number: &id001
          type:
          - string
          - 'null'
        internal_code: *id001
        bus_name: *id001
        seats_count: &id002
          type:
          - integer
          - 'null'
        brand: *id001
        model: *id001
        year: *id002
        current_status: *id001
        status: *id001
        current_route_id: *id002
        last_gps_at: *id001
    Route:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        code: *id001
        start_name: *id001
        end_name: *id001
        distance_estimate: &id003
          type:
          - number
          - 'null'
        duration_estimate: *id002
        status: *id001
    RouteStop:
      type: object
      properties:
        id:
          type: integer
        route_id:
          type: integer
        stop_name:
          type: string
        stop_order:
          type: integer
        latitude: *id003
        longitude: *id003
        is_start:
          type:
          - boolean
          - integer
        is_end:
          type:
          - boolean
          - integer
        status: *id001
    Trip:
      type: object
      properties:
        id:
          type: integer
        route_id: *id002
        bus_id: *id002
        trip_date: *id001
        direction: *id001
        trip_type: *id001
        service_kind: *id001
        planned_departure_at: *id001
        planned_arrival_at: *id001
        actual_departure_at: *id001
        actual_arrival_at: *id001
        status: *id001
        delay_minutes: *id002
        route_name: *id001
        route_code: *id001
        plate_number: *id001
        bus_name: *id001
    BusLocation:
      type: object
      properties:
        id:
          type: integer
        plate_number: *id001
        internal_code: *id001
        bus_name: *id001
        last_latitude: *id003
        last_longitude: *id003
        last_gps_at: *id001
        last_speed_kmh: *id003
        last_heading: *id003
        last_accuracy: *id003
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          const: false
        code:
          type: string
        trace_id:
          type: string
        message:
          type: string
        data:
          type: 'null'
        errors:
          type:
          - object
          - array
        meta:
          type: object
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
            retryable:
              type: boolean
            correlation_id:
              type: string
    StatusResponse:
      type: object
      required:
      - success
      - code
      - data
      - meta
      properties:
        success:
          type: boolean
          const: true
        code:
          type: string
          const: OK
        trace_id:
          type: string
        message:
          type: string
        data:
          type: object
          properties:
            plan:
              type: string
              enum:
              - API Standard
              - API Business
            level:
              type: string
              enum:
              - standard
              - business
            scopes:
              type: array
              items:
                type: string
            rate_limit_per_minute:
              type: integer
        errors:
          type: array
        meta:
          $ref: '#/components/schemas/Meta'
    BusesResponse:
      type: object
      required:
      - success
      - code
      - data
      - meta
      properties:
        success:
          type: boolean
          const: true
        code:
          type: string
          const: OK
        trace_id:
          type: string
        message:
          type: string
        data:
          type: object
          properties:
            items:
              type: array
              items:
                $ref: '#/components/schemas/Bus'
            pagination:
              $ref: '#/components/schemas/Pagination'
        errors:
          type: array
        meta:
          $ref: '#/components/schemas/Meta'
    RoutesResponse:
      type: object
      required:
      - success
      - code
      - data
      - meta
      properties:
        success:
          type: boolean
          const: true
        code:
          type: string
          const: OK
        trace_id:
          type: string
        message:
          type: string
        data:
          type: object
          properties:
            items:
              type: array
              items:
                $ref: '#/components/schemas/Route'
            pagination:
              $ref: '#/components/schemas/Pagination'
        errors:
          type: array
        meta:
          $ref: '#/components/schemas/Meta'
    RouteStopsResponse:
      type: object
      required:
      - success
      - code
      - data
      - meta
      properties:
        success:
          type: boolean
          const: true
        code:
          type: string
          const: OK
        trace_id:
          type: string
        message:
          type: string
        data:
          type: object
          properties:
            route:
              type: object
            items:
              type: array
              items:
                $ref: '#/components/schemas/RouteStop'
        errors:
          type: array
        meta:
          $ref: '#/components/schemas/Meta'
    TripsResponse:
      type: object
      required:
      - success
      - code
      - data
      - meta
      properties:
        success:
          type: boolean
          const: true
        code:
          type: string
          const: OK
        trace_id:
          type: string
        message:
          type: string
        data:
          type: object
          properties:
            items:
              type: array
              items:
                $ref: '#/components/schemas/Trip'
            pagination:
              $ref: '#/components/schemas/Pagination'
        errors:
          type: array
        meta:
          $ref: '#/components/schemas/Meta'
    TripResponse:
      type: object
      required:
      - success
      - code
      - data
      - meta
      properties:
        success:
          type: boolean
          const: true
        code:
          type: string
          const: OK
        trace_id:
          type: string
        message:
          type: string
        data:
          $ref: '#/components/schemas/Trip'
        errors:
          type: array
        meta:
          $ref: '#/components/schemas/Meta'
    BusLocationResponse:
      type: object
      required:
      - success
      - code
      - data
      - meta
      properties:
        success:
          type: boolean
          const: true
        code:
          type: string
          const: OK
        trace_id:
          type: string
        message:
          type: string
        data:
          $ref: '#/components/schemas/BusLocation'
        errors:
          type: array
        meta:
          $ref: '#/components/schemas/Meta'
  responses:
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Forbidden:
      description: Plan or scope denied
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
paths:
  /status:
    get:
      tags:
      - Integration
      summary: Integration status
      parameters: []
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
  /buses:
    get:
      tags:
      - Fleet
      summary: List buses
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/PerPage'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BusesResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      description: 'Required scope: `buses:read`.'
  /routes:
    get:
      tags:
      - Network
      summary: List routes
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/PerPage'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoutesResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      description: 'Required scope: `routes:read`.'
  /routes/{id}/stops:
    get:
      tags:
      - Network
      summary: Route stops
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
          minimum: 1
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RouteStopsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '404':
          $ref: '#/components/responses/NotFound'
      description: 'Required scope: `stops:read`.'
  /trips/active:
    get:
      tags:
      - Trips
      summary: Active trips
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/PerPage'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
      description: 'Required scope: `trips:read`.'
  /trips/{id}:
    get:
      tags:
      - Trips
      summary: Trip detail
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
          minimum: 1
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TripResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '404':
          $ref: '#/components/responses/NotFound'
      description: 'Required scope: `trips:read`.'
  /buses/{id}/location:
    get:
      tags:
      - Fleet
      summary: Last known bus location
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
          minimum: 1
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BusLocationResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '404':
          $ref: '#/components/responses/NotFound'
      description: 'Required scope: `gps:read`.'
x-masar-scopes:
  buses:read: Read buses
  routes:read: Read routes
  stops:read: Read route stops
  trips:read: Read trips
  gps:read: Read last known bus location
