openapi: 3.0.3
info:
  title: Almighty Dispatch Partner API
  version: "1.2"
  description: >
    Quote a fare, create a booking and check its status in one operator's
    Almighty Dispatch account. The operator creates your API key in Dispatch
    (Settings, Integrations, Dispatch API). Send it in a header, never in the URL.
    Limits: 120 requests per minute and 60 new bookings per hour per key.
    The operator can also give your key a webhook address: signed POSTs are then sent when a booking changes (see the webhooks section of the PDF/Markdown documentation and the BookingEvent schema).
servers:
  - url: https://www.almightydispatch.com/api/v1
security:
  - ApiKeyHeader: []
  - BearerAuth: []
paths:
  /fare:
    post:
      summary: Quote a fare
      description: Same pricing engine that charges cards on the operator's own booking widget. Omit vehicleClassId to quote every active vehicle class (this is also how you discover the class ids).
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/FareRequest" }
      responses:
        "200":
          description: Fares per vehicle class
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FareResponse" }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /bookings:
    post:
      summary: Create a booking
      description: Creates a Pending booking. The fare is always computed by the server; any price you send is ignored. Sending the same externalRef again returns the first booking (duplicate true) instead of creating another.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CreateBookingRequest" }
      responses:
        "201":
          description: Booking created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CreateBookingResponse" }
        "200":
          description: This externalRef was already booked; the original result is returned
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CreateBookingResponse" }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
        "409": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/RateLimited" }
    get:
      summary: Check a booking
      description: Only bookings created with your own key can be looked up. Give either ref or externalRef.
      parameters:
        - name: ref
          in: query
          schema: { type: integer }
          description: The operator's booking number (from the create response).
        - name: externalRef
          in: query
          schema: { type: string, maxLength: 100 }
          description: Your own reference.
      responses:
        "200":
          description: Booking status
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BookingStatusResponse" }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /requests:
    post:
      summary: Ask to cancel or change a booking
      description: Nothing changes until the operator approves. A test key (tdk_test_) is approved automatically.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ChangeRequest" }
      responses:
        "201":
          description: Request recorded
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ChangeRequestStatus" }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
        "409": { $ref: "#/components/responses/Error" }
        "429": { $ref: "#/components/responses/RateLimited" }
    get:
      summary: Check a request
      parameters:
        - name: id
          in: query
          schema: { type: string }
        - name: ref
          in: query
          schema: { type: integer }
          description: Booking number; returns the latest 20 requests for it.
      responses:
        "200":
          description: Request status
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ChangeRequestStatus" }
        "400": { $ref: "#/components/responses/Error" }
        "401": { $ref: "#/components/responses/Error" }
        "404": { $ref: "#/components/responses/Error" }
components:
  securitySchemes:
    ApiKeyHeader: { type: apiKey, in: header, name: x-api-key }
    BearerAuth: { type: http, scheme: bearer }
  responses:
    Error:
      description: Error
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    RateLimited:
      description: Rate limit reached. Wait for the Retry-After seconds and retry.
      headers:
        Retry-After: { schema: { type: integer } }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Error:
      type: object
      properties:
        error: { type: string }
    FareRequest:
      type: object
      required: [pickup, dropoff]
      properties:
        pickup: { type: string, maxLength: 300, description: Full address with postcode }
        dropoff: { type: string, maxLength: 300 }
        distance: { type: number, minimum: 0, description: Road distance in the operator's unit (miles for UK) }
        pickupLat: { type: number }
        pickupLng: { type: number }
        dropoffLat: { type: number }
        dropoffLng: { type: number }
        date: { type: string, format: date, example: "2026-11-02" }
        time: { type: string, example: "06:30" }
        vehicleClassId: { type: string }
        promoCode: { type: string }
    Breakdown:
      type: object
      properties:
        label: { type: string, example: "32 mi x 1x" }
        amount: { type: number, example: 110.4 }
    FareResponse:
      type: object
      properties:
        night: { type: boolean }
        fares:
          type: array
          items:
            type: object
            properties:
              vehicleClassId: { type: string }
              name: { type: string, example: Saloon }
              seats: { type: integer }
              total: { type: number, example: 118.4 }
              breakdown:
                type: array
                items: { $ref: "#/components/schemas/Breakdown" }
    CreateBookingRequest:
      type: object
      required: [customer, booking]
      properties:
        customer:
          type: object
          required: [name, phone]
          properties:
            name: { type: string, maxLength: 120 }
            phone: { type: string, maxLength: 30, example: "+447700900123" }
            email: { type: string }
        booking:
          type: object
          required: [pickup, dropoff, pickupLat, pickupLng, dropoffLat, dropoffLng, distance, date, time, vehicleClassId, externalRef]
          properties:
            pickup: { type: string, maxLength: 300 }
            dropoff: { type: string, maxLength: 300 }
            pickupLat: { type: number }
            pickupLng: { type: number }
            dropoffLat: { type: number }
            dropoffLng: { type: number }
            distance: { type: number, exclusiveMinimum: true, minimum: 0, description: "Must be at least 95% of the straight line between the coordinates and at most 3x it plus 10" }
            date: { type: string, format: date }
            time: { type: string, example: "06:30" }
            vehicleClassId: { type: string }
            externalRef: { type: string, maxLength: 100, description: Your own unique reference. Makes retries safe. }
            stops:
              type: array
              items: { type: string }
            passengers: { type: integer }
            bags: { type: integer }
            luggage: { type: integer }
            flightNo: { type: string }
            notes: { type: string }
            isReturn: { type: boolean }
            returnDate: { type: string, format: date }
            returnTime: { type: string }
            paymentMethod: { type: string, enum: [Cash, Card on Arrival], default: Cash }
    CreateBookingResponse:
      type: object
      properties:
        customerId: { type: string }
        bookings:
          type: array
          items:
            type: object
            properties:
              ref: { type: integer, example: 6141 }
              returnRef: { type: integer, nullable: true }
              bookingId: { type: string }
              vehicleId: { type: string }
              vehicleName: { type: string }
        fare: { type: number, description: Per leg }
        breakdown:
          type: array
          items: { $ref: "#/components/schemas/Breakdown" }
        duplicate: { type: boolean, description: "True only when this externalRef was already booked" }
    BookingStatusResponse:
      type: object
      properties:
        ref: { type: integer }
        status:
          type: string
          enum: [Pending, Pool, Pool-Claimed, Confirmed, Dispatched, Driver Accepted, Arrived, In Progress, Completed, Cancelled]
        date: { type: string }
        time: { type: string, description: "May include seconds; read the first five characters" }
        pickup: { type: string }
        dropoff: { type: string }
        vehicleName: { type: string }
        fare: { type: number }
        paymentMethod: { type: string }
        isReturn: { type: boolean }
        returnDate: { type: string, nullable: true }
        returnTime: { type: string, nullable: true }
        driver:
          type: object
          nullable: true
          description: Present once a driver is allocated. Phone only if the operator enabled it for your key.
          properties:
            firstName: { type: string }
            phone: { type: string }
        vehicle:
          type: object
          nullable: true
          properties:
            make: { type: string }
            model: { type: string }
            colour: { type: string }
            registration: { type: string }
    BookingEvent:
      type: object
      description: Body POSTed to your webhook address. Header X-AD-Signature is t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.rawBody" keyed with your signing secret>. Reply 2xx within 8 seconds.
      properties:
        id: { type: string, description: Unique per message; also in header X-AD-Delivery. Use it to ignore repeats. }
        createdAt: { type: string, format: date-time }
        event:
          type: string
          enum: [booking.confirmed, booking.driver_allocated, booking.driver_accepted, booking.driver_changed, booking.driver_arrived, booking.passenger_on_board, booking.completed, booking.cancelled, booking.updated, request.approved, request.rejected, webhook.test]
        booking:
          type: object
          properties:
            ref: { type: integer }
            externalRef: { type: string }
            status: { type: string }
            date: { type: string }
            time: { type: string }
            pickup: { type: string }
            dropoff: { type: string }
            vehicleName: { type: string }
        driver:
          type: object
          nullable: true
          properties:
            firstName: { type: string }
            phone: { type: string }
        vehicle:
          type: object
          nullable: true
          properties:
            make: { type: string }
            model: { type: string }
            colour: { type: string }
            registration: { type: string }
    ChangeRequest:
      type: object
      required: [type]
      properties:
        type: { type: string, enum: [cancel, amend] }
        ref: { type: integer }
        externalRef: { type: string }
        reason: { type: string, maxLength: 500 }
        changes:
          type: object
          description: For amend only. Allowed keys are date, time, pickup, dropoff, passengers, bags, luggage, flightNo, notes, stops.
    ChangeRequestStatus:
      type: object
      properties:
        id: { type: string }
        type: { type: string, enum: [cancel, amend] }
        status: { type: string, enum: [pending, approved, rejected] }
        ref: { type: integer }
        externalRef: { type: string }
        note: { type: string, nullable: true }
        createdAt: { type: string, format: date-time }
        decidedAt: { type: string, format: date-time, nullable: true }
