openapi: 3.1.0
info:
  title: Aero-Plan Customer API
  description: 'The Aero-Plan Customer API enables insurance companies, hospitals,
    and assistance providers to integrate medical transport requests directly into
    their existing systems. Create quote requests, compare proposals from vetted transport
    providers, and manage the entire booking process — streamlining operations and
    reducing response times for critical patient transfers.


    ## Authentication


    This API requires OAuth 2.0 authentication. You must obtain an access token before
    making requests to this API.


    To authenticate:

    1. Register your application to obtain client credentials

    2. Use the OAuth 2.0 client credentials flow to obtain an access token

    3. Include the access token in the Authorization header of your requests: `Authorization:
    Bearer {access_token}`


    Contact team@aero-plan.com to register your application and receive your client
    credentials.


    ## Webhooks (Coming Soon)


    Webhook functionality will be available in the near future to notify your application
    in real-time about quote-related events.


    ### Planned Webhook Events

    - `quote.created` - Triggered when a service provider submits a new quote for
    your request

    - `quote.updated` - Triggered when a service provider modifies an existing quote

    - `quote.deleted` - Triggered when a service provider withdraws or deletes a
    quote


    **Note:** Exact payload structure, authentication mechanism, configuration process,
    and retry logic are currently being finalized and are subject to change.

    '
  version: 1.0.0
servers:
- url: https://api.aero-plan.com/customers
  description: Production server
- url: https://sandbox-api.aero-plan.com/customers
  description: Sandbox server
tags:
- name: Requests
  description: Quote request operations
paths:
  /v1/requests:
    post:
      tags:
      - Requests
      summary: Create a new quote request
      description: 'Submit a new medical transport quote request. This will create
        a request that service providers can then submit quotes for.


        Required fields depend on the type of service requested. See the request body
        schema for full details.

        '
      operationId: createQuoteRequest
      security:
      - oauth2:
        - requests:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateQuoteRequestInput'
            examples:
              minimal_aa:
                $ref: '#/components/examples/MinimalAirAmbulanceRequest'
              full_cme:
                $ref: '#/components/examples/FullCommercialEscortRequest'
      responses:
        '201':
          description: Quote request created successfully
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/QuoteRequestResponse.yaml'
        '400':
          description: Invalid request - validation errors or missing required fields
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/ErrorResponse.yaml'
        '401':
          description: Authentication failed - invalid or expired access token
        '403':
          description: Insufficient permissions - requires `requests:write` scope
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/ErrorResponse.yaml'
    get:
      tags:
      - Requests
      summary: List quote requests
      description: Retrieve a paginated list of quote requests created by the authenticated
        customer.
      operationId: listQuoteRequests
      security:
      - oauth2:
        - requests:read
      parameters:
      - name: page
        in: query
        description: Page number for pagination (1-based)
        schema:
          type: integer
          minimum: 1
          default: 1
        example: 1
      - name: per_page
        in: query
        description: Number of results per page (max 100)
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
        example: 25
      - name: status
        in: query
        description: Filter by request status
        schema:
          type: string
          enum:
          - draft
          - active
          - accepted
          - confirmed
          - paid
          - canceled
        example: active
      responses:
        '200':
          description: Successful response with list of quote requests
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '../shared/schemas/QuoteRequestResponse.yaml'
                  meta:
                    type: object
                    properties:
                      current_page:
                        type: integer
                        example: 1
                      per_page:
                        type: integer
                        example: 25
                      total_pages:
                        type: integer
                        example: 4
                      total_count:
                        type: integer
                        example: 89
        '401':
          description: Authentication failed
        '403':
          description: Insufficient permissions
  /v1/requests/{request_id}:
    get:
      tags:
      - Requests
      summary: Get a specific quote request
      description: Retrieve detailed information about a specific quote request, including
        all associated quotes.
      operationId: getQuoteRequest
      security:
      - oauth2:
        - requests:read
      parameters:
      - name: request_id
        in: path
        required: true
        description: Unique identifier of the quote request
        schema:
          type: integer
        example: 12345
      responses:
        '200':
          description: Successful response with quote request details and quotes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuoteRequestWithQuotesResponse'
        '401':
          description: Authentication failed
        '403':
          description: Insufficient permissions
        '404':
          description: Quote request not found
  /v1/requests/{request_id}/quotes/{quote_id}:
    get:
      tags:
      - Requests
      summary: Get a specific quote
      description: Retrieve detailed information about a specific quote submitted
        for a quote request.
      operationId: getQuote
      security:
      - oauth2:
        - requests:read
      parameters:
      - name: request_id
        in: path
        required: true
        description: Unique identifier of the quote request
        schema:
          type: integer
        example: 12345
      - name: quote_id
        in: path
        required: true
        description: Unique identifier of the quote
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440001
      responses:
        '200':
          description: Successful response with full quote details
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/QuoteDetail.yaml'
        '401':
          description: Authentication failed
        '403':
          description: Insufficient permissions
        '404':
          description: Quote or quote request not found
  /v1/requests/{request_id}/quotes/{quote_id}/accept:
    post:
      tags:
      - Requests
      summary: Accept a quote
      description: 'Accept a specific quote for a quote request. This will mark the
        quote as accepted and notify the service provider.


        Only one quote can be accepted per request. Accepting a quote will automatically
        reject all other active quotes for the same request.

        '
      operationId: acceptQuote
      security:
      - oauth2:
        - requests:write
      parameters:
      - name: request_id
        in: path
        required: true
        description: Unique identifier of the quote request
        schema:
          type: integer
        example: 12345
      - name: quote_id
        in: path
        required: true
        description: Unique identifier of the quote to accept
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440001
      responses:
        '200':
          description: Quote accepted successfully
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/QuoteDetail.yaml'
        '401':
          description: Authentication failed
        '403':
          description: Insufficient permissions
        '404':
          description: Quote or quote request not found
        '409':
          description: Quote cannot be accepted (already accepted, withdrawn, or request
            is not in an active state)
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/ErrorResponse.yaml'
  /v1/requests/{request_id}/quotes/{quote_id}/reject:
    post:
      tags:
      - Requests
      summary: Reject a quote
      description: 'Reject a specific quote for a quote request. This will mark the
        quote as rejected and notify the service provider.


        Rejected quotes cannot be accepted later.

        '
      operationId: rejectQuote
      security:
      - oauth2:
        - requests:write
      parameters:
      - name: request_id
        in: path
        required: true
        description: Unique identifier of the quote request
        schema:
          type: integer
        example: 12345
      - name: quote_id
        in: path
        required: true
        description: Unique identifier of the quote to reject
        schema:
          type: string
        example: 550e8400-e29b-41d4-a716-446655440001
      responses:
        '200':
          description: Quote rejected successfully
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/QuoteDetail.yaml'
        '401':
          description: Authentication failed
        '403':
          description: Insufficient permissions
        '404':
          description: Quote or quote request not found
        '409':
          description: Quote cannot be rejected (already rejected, withdrawn, or request
            is not in an active state)
          content:
            application/json:
              schema:
                $ref: '../shared/schemas/ErrorResponse.yaml'
webhooks:
  quoteCreated:
    post:
      summary: Quote Created
      description: Triggered when a service provider submits a new quote for your
        request.
      operationId: webhookQuoteCreated
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                event:
                  type: string
                  const: quote.created
                  description: Event type identifier
                request_id:
                  type: integer
                  description: The quote request this quote belongs to
                quote_id:
                  type: string
                  description: Unique identifier for the new quote
                timestamp:
                  type: string
                  format: date-time
                  description: When the event occurred
                data:
                  $ref: '../shared/schemas/QuoteSummary.yaml'
            example:
              event: quote.created
              request_id: 12345
              quote_id: 550e8400-e29b-41d4-a716-446655440001
              timestamp: '2024-10-30T11:15:00Z'
              data:
                id: 550e8400-e29b-41d4-a716-446655440001
                status: ACTIVE
                price: 45000.00
                currency: EUR
                submitted_at: '2024-10-30T11:15:00Z'
                updated_at: '2024-10-30T11:15:00Z'
                earliest_patient_pickup_at: '2024-10-30T14:00:00Z'
                time_to_reach_patient_minutes: 45
                suggested_eta: '2024-10-30T16:30:00Z'
                suggested_route: JFK → LAX
                number_of_legs: 1
                aircraft: Learjet 45
                flight_time_estimate_minutes: 180
                ground_transport: true
                quote_valid_until: '2024-10-31T11:15:00Z'
      responses:
        '200':
          description: Webhook received successfully
        '400':
          description: Bad request - invalid payload
        '401':
          description: Unauthorized - invalid webhook signature
        '500':
          description: Internal server error
  quoteUpdated:
    post:
      summary: Quote Updated
      description: Triggered when a service provider modifies an existing quote for
        your request.
      operationId: webhookQuoteUpdated
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                event:
                  type: string
                  const: quote.updated
                  description: Event type identifier
                request_id:
                  type: integer
                  description: The quote request this quote belongs to
                quote_id:
                  type: string
                  description: Unique identifier for the updated quote
                timestamp:
                  type: string
                  format: date-time
                  description: When the event occurred
                data:
                  $ref: '../shared/schemas/QuoteSummary.yaml'
            example:
              event: quote.updated
              request_id: 12345
              quote_id: 550e8400-e29b-41d4-a716-446655440001
              timestamp: '2024-10-30T12:00:00Z'
              data:
                id: 550e8400-e29b-41d4-a716-446655440001
                status: ACTIVE
                price: 42000.00
                currency: EUR
                submitted_at: '2024-10-30T11:15:00Z'
                updated_at: '2024-10-30T12:00:00Z'
                earliest_patient_pickup_at: '2024-10-30T13:30:00Z'
                time_to_reach_patient_minutes: 30
                suggested_eta: '2024-10-30T16:00:00Z'
                suggested_route: JFK → LAX
                number_of_legs: 1
                aircraft: Learjet 45
                flight_time_estimate_minutes: 180
                ground_transport: true
                quote_valid_until: '2024-10-31T12:00:00Z'
      responses:
        '200':
          description: Webhook received successfully
        '400':
          description: Bad request - invalid payload
        '401':
          description: Unauthorized - invalid webhook signature
        '500':
          description: Internal server error
  quoteDeleted:
    post:
      summary: Quote Deleted
      description: Triggered when a service provider withdraws or deletes a quote
        for your request.
      operationId: webhookQuoteDeleted
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                event:
                  type: string
                  const: quote.deleted
                  description: Event type identifier
                request_id:
                  type: integer
                  description: The quote request this quote belonged to
                quote_id:
                  type: string
                  description: Unique identifier for the deleted quote
                timestamp:
                  type: string
                  format: date-time
                  description: When the event occurred
                data:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Unique identifier for the deleted quote
                    status:
                      type: string
                      const: WITHDRAWN
                      description: Status after deletion
                    deleted_at:
                      type: string
                      format: date-time
                      description: When the quote was deleted
                      example: '2024-10-30T13:00:00Z'
            example:
              event: quote.deleted
              request_id: 12345
              quote_id: 550e8400-e29b-41d4-a716-446655440002
              timestamp: '2024-10-30T13:00:00Z'
              data:
                id: 550e8400-e29b-41d4-a716-446655440002
                status: WITHDRAWN
                deleted_at: '2024-10-30T13:00:00Z'
      responses:
        '200':
          description: Webhook received successfully
        '400':
          description: Bad request - invalid payload
        '401':
          description: Unauthorized - invalid webhook signature
        '500':
          description: Internal server error
components:
  securitySchemes:
    oauth2:
      type: oauth2
      description: OAuth 2.0 authentication is required to access this API
      flows:
        clientCredentials:
          tokenUrl: https://api.aero-plan.com/customers/oauth/token
          scopes:
            requests:write: Create and manage quote requests from server-to-server
              integrations
            requests:read: Read quote requests and associated quotes
        authorizationCode:
          authorizationUrl: https://api.aero-plan.com/customers/oauth/authorize
          tokenUrl: https://api.aero-plan.com/customers/oauth/token
          scopes:
            requests:write: Create and manage quote requests on behalf of a user
            requests:read: Read quote requests and associated quotes
  schemas:
    CreateQuoteRequestInput:
      type: object
      required:
      - requested_service
      - payer_type
      - patients
      - transfer_eta_type
      properties:
        requested_service:
          type: string
          enum:
          - aa
          - cme
          description: 'Type of service requested:

            - `aa` - Air Ambulance

            - `cme` - Commercial Medical Escort

            '
          example: aa
        payer_type:
          type: string
          enum:
          - insurance
          - patient
          - partial_patient
          description: 'Who will be paying for the service:

            - `insurance` - Insurance company pays

            - `patient` - Patient pays (details communicated after quote acceptance)

            - `partial_patient` - Partial patient responsibility (details in additional_info)

            '
          example: insurance
        pricing:
          type: string
          enum:
          - all_inclusive
          - clinician_fees_only
          description: 'Pricing model (mandatory when requested_service is ''cme''):

            - `all_inclusive` - All-inclusive pricing

            - `clinician_fees_only` - Clinician fees only

            '
          example: all_inclusive
        include_ground_transport:
          type: boolean
          description: Include ground transport in the quote (departure and arrival)
          default: false
          example: true
        ground_transport_type:
          type: string
          enum:
          - taxi
          - wc_van
          - bls_ambulance
          - als_ambulance
          - tbd
          description: 'Type of ground transport (required if include_ground_transport
            is true):

            - `taxi` - Taxi / Towncar

            - `wc_van` - Wheelchair accessible van

            - `bls_ambulance` - Basic Life Support Ambulance

            - `als_ambulance` - Advanced Life Support Ambulance

            - `tbd` - To be determined by provider

            '
          example: bls_ambulance
        patients:
          type: array
          minItems: 1
          description: List of patients for this transport (minimum 1 required)
          items:
            $ref: '../shared/schemas/PatientInfo.yaml'
        transfer_eta_type:
          type: string
          enum:
          - asap
          - at
          - tbd
          - evac
          description: 'Estimated arrival time urgency:

            - `asap` - As soon as possible

            - `at` - Specific date/time (requires transfer_eta_at)

            - `tbd` - To be determined

            - `evac` - Emergency evacuation

            '
          example: asap
        transfer_eta_at:
          type: string
          format: date-time
          description: Specific arrival date and time in UTC (required if transfer_eta_type
            is 'at'). Must be in ISO 8601 format with 'Z' suffix.
          example: '2024-11-15T14:00:00Z'
        required_team_tbd:
          type: boolean
          description: Whether the required team will be determined by the service
            provider
          default: false
          example: false
        required_team:
          type: array
          description: 'Specific team members required (required for CME, optional
            for AA unless required_team_tbd is true).

            At least one team member must be specified if required_team_tbd is false.

            '
          items:
            type: string
            enum:
            - physician
            - nurse
            - medic
            - respirator
            - non_medical_escort
          example:
          - physician
          - nurse
        required_team_other:
          type: string
          minLength: 2
          maxLength: 255
          description: Other team requirements not covered by standard options
          example: Pediatric specialist
        allow_pooling:
          type: boolean
          nullable: true
          description: 'Whether to get pooling possibilities for this patient.

            Note: Patient age and diagnosis will be shared with provider if enabled.

            '
          example: false
        contact_name:
          type: string
          description: Contact person name
          example: John Smith
        contact_phone:
          type: string
          minLength: 2
          maxLength: 255
          description: Contact phone number (include country code)
          example: +1-555-0123-3333
        contact_email:
          type: string
          format: email
          description: Contact email address
          example: john.smith@insurance.com
        contact_relation:
          type: string
          minLength: 2
          maxLength: 255
          description: Contact's relation to patient (e.g., Case Manager, Family)
          example: Case Manager
        payment_information:
          type: string
          description: Payment details and instructions
          example: 'LOA #123456 - United Healthcare'
        billing_email:
          type: string
          format: email
          description: Email address for billing purposes
          example: billing@insurance.com
        additional_info:
          type: string
          minLength: 2
          maxLength: 5000
          description: Any additional information or special requirements
          example: Patient requires cardiac monitoring throughout transport
        external_recipients_should_be_emailed:
          type: boolean
          description: Send to service providers outside the Aero-Plan network (if
            enabled)
          default: false
          example: false
        external_service_provider_emails_delimited:
          type: string
          description: Comma-separated email addresses for external service providers
          example: provider1@example.com,provider2@example.com
    QuoteRequestWithQuotesResponse:
      allOf:
      - $ref: '../shared/schemas/QuoteRequestResponse.yaml'
      - type: object
        properties:
          patients:
            type: integer
            minimum: 1
            description: Number of patients in this request
            example: 1
          quotes:
            type: array
            description: List of quote summaries submitted by service providers for
              this request
            items:
              $ref: '../shared/schemas/QuoteSummary.yaml'
            example:
            - id: 550e8400-e29b-41d4-a716-446655440001
              status: ACTIVE
              price: 45000.00
              currency: EUR
              submitted_at: '2024-10-30T11:15:00Z'
              updated_at: '2024-10-30T11:15:00Z'
              earliest_patient_pickup_at: '2024-10-30T14:00:00Z'
              time_to_reach_patient_minutes: 45
              suggested_eta: '2024-10-30T16:30:00Z'
              suggested_route: JFK → LAX
              number_of_legs: 1
              aircraft: Learjet 45
              flight_time_estimate_minutes: 180
              ground_transport: true
              quote_valid_until: '2024-10-31T11:15:00Z'
  examples:
    MinimalAirAmbulanceRequest:
      summary: Minimal Air Ambulance Request
      description: Minimum required fields for an AA request
      value:
        requested_service: aa
        payer_type: insurance
        patients:
        - patient_age: 50
          transfer_from: Miami, FL, USA
          transfer_to: Boston, MA, USA
          med_diagnosis: Stroke requiring immediate specialized care
        transfer_eta_type: asap
        required_team_tbd: true
    FullCommercialEscortRequest:
      summary: Complete Commercial Medical Escort Request
      description: Fully populated CME request with all optional fields
      value:
        requested_service: cme
        pricing: all_inclusive
        payer_type: patient
        include_ground_transport: true
        ground_transport_type: taxi
        patients:
        - case_number: CME-2024-100
          patient_nationality: Canada
          patient_age: 72
          patient_height: 165cm
          patient_weight: 70kg
          patient_extra_wide: false
          transfer_from: Toronto, ON, Canada
          transfer_to: Vancouver, BC, Canada
          transfer_facility: Toronto General Hospital
          transfer_receiving_bed: true
          seating_accommodations: business
          extra_seat: false
          transfer_extra_passengers: 1
          upgrade_travel_companion: true
          oxygen_concentrator: true
          med_diagnosis: Post-operative recovery from cardiac surgery requiring medical
            supervision
          med_treatment: Anticoagulation therapy, pain management
          med_concomitant: COPD, osteoarthritis
          med_fit_to_fly: true
        transfer_eta_type: at
        transfer_eta_at: '2024-11-20T09:00:00Z'
        required_team:
        - nurse
        allow_pooling: false
        contact_name: Sarah Johnson
        contact_phone: +1-416-555-0199
        contact_email: sarah.johnson@email.com
        contact_relation: Daughter
        payment_information: Self-pay - credit card on file
        billing_email: sarah.johnson@email.com
        additional_info: Patient requires window seat and assistance with mobility
security:
- oauth2:
  - requests:read
  - requests:write
