openapi: 3.1.0
info:
  title: RentADriver API
  version: 1.0.0
  summary: Physical delivery for AI agents and apps, fulfilled by vetted
    BusyDrivers drivers.
  description: "Quote → create → fund → track → prove → pay. Quotes and coverage
    are free without auth. Everything else needs an API key (x-api-key). MCP
    server: `npx -y rentadriver-mcp`. Money is in integer minor units (cents)."
  contact:
    email: support@rentadriver.ai
    url: https://rentadriver.ai
  x-logo:
    url: https://rentadriver.ai/brand/icon.svg
servers:
  - url: https://api.rentadriver.ai
tags:
  - name: Discovery
  - name: Quotes
  - name: Deliveries
  - name: Tracking
  - name: Account
  - name: Wallet
  - name: Webhooks
  - name: Shopify
    description: The RentADriver app for Shopify
      (rentadriver.ai/integrations/shopify). Merchants install it from Shopify
      admin; these endpoints let the account that owns the shop list and book
      Shopify orders. Shopify-facing endpoints (install, session, webhooks,
      carrier-service rates) are documented at
      rentadriver.ai/integrations/shopify.
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
  schemas:
    Stop: &a1
      type: object
      required: []
      properties:
        address:
          type: string
          description: Street address; geocoded server-side if lat/lng omitted
        lat:
          type: number
        lng:
          type: number
        contact_name:
          type: string
        contact_phone:
          type: string
        instructions:
          type: string
        window_start:
          type: string
          format: date-time
        window_end:
          type: string
          format: date-time
        proof_required:
          type: array
          items:
            type: string
            enum:
              - photo
              - signature
              - recipient_name
              - otp
              - id_check
    Item: &a2
      type: object
      required:
        - description
      properties:
        description:
          type: string
        quantity:
          type: integer
          default: 1
        size_class:
          type: string
          enum:
            - S
            - M
            - L
            - XL
          default: M
          description: S envelope/bag · M shoebox–backpack · L suitcase · XL
            furniture/pallet
        weight_kg:
          type: number
        value_cents:
          type: integer
        category:
          type: string
          enum:
            - parcel
            - document
            - retail
            - food
            - grocery
            - flowers
            - pharmacy
            - keys
            - electronics
            - other
        fragile:
          type: boolean
    QuoteRequest: &a3
      type: object
      required:
        - pickup
        - dropoffs
      properties:
        pickup: *a1
        dropoffs:
          type: array
          minItems: 1
          maxItems: 8
          items: *a1
        service:
          type: string
          enum:
            - auto
            - local
            - intercity
          default: auto
          description: auto picks intercity when the drop-off is in another launch city
        speed:
          type: string
          enum:
            - express
            - standard
            - shared
          default: standard
          description: "Intercity only: express = dedicated driver leaves within 2h;
            standard = dedicated within 24h; shared = consolidated lane run at
            fixed departures (cheapest)"
        items:
          type: array
          items: *a2
        urgency:
          type: string
          enum:
            - asap
            - today
            - scheduled
          default: asap
        scheduled_at:
          type: string
          format: date-time
        vehicle_class:
          type: string
          enum:
            - walk
            - bike
            - scooter
            - car
            - suv
            - van
            - truck
    CreateDeliveryRequest: &a4
      allOf:
        - *a3
      type: object
      properties:
        quote_id:
          type: string
          format: uuid
          description: Use a quote from POST /v1/quotes instead of re-sending stops
        instructions:
          type: string
        contingency:
          type: object
          properties:
            if_no_answer:
              type: string
              enum:
                - wait_then_leave_safe
                - wait_then_return
                - call_client
                - leave_with_neighbour
            if_closed:
              type: string
              enum:
                - return_to_pickup
                - hold_and_retry
                - call_client
            max_wait_minutes:
              type: integer
            return_to:
              type: string
            notes:
              type: string
        external_ref:
          type: string
        idempotency_key:
          type: string
        webhook_url:
          type: string
          format: uri
        metadata:
          type: object
        fund:
          type: boolean
          default: true
          description: Hold funds from the wallet and start dispatch immediately
        dryRun:
          type: boolean
          default: false
          description: Validate + price only
    Delivery: &a5
      type: object
      properties:
        id:
          type: string
        short_code:
          type: string
          example: RD-7K2P9QX
        status:
          type: string
          enum:
            - quoted
            - pending_funding
            - funded
            - dispatching
            - assigned
            - en_route_pickup
            - at_pickup
            - picked_up
            - en_route_dropoff
            - at_dropoff
            - delivered
            - confirmed
            - paid
            - no_driver_found
            - cancelled
            - failed
            - returned
            - disputed
        next_action:
          type: string
        currency:
          type: string
        price_cents:
          type: integer
        driver_pay_cents:
          type: integer
        tip_cents:
          type: integer
        eta_pickup_at:
          type: string
          format: date-time
          nullable: true
        eta_dropoff_at:
          type: string
          format: date-time
          nullable: true
        stops:
          type: array
          items:
            type: object
        driver:
          type: object
          nullable: true
          properties:
            id:
              type: string
            first_name:
              type: string
            rating:
              type: number
            jobs_completed:
              type: integer
            vehicle_class:
              type: string
            lat:
              type: number
            lng:
              type: number
        proof:
          type: object
        tracking_url:
          type: string
        created_at:
          type: string
          format: date-time
paths:
  /v1/intercity/lanes:
    get:
      tags:
        - Discovery
      summary: Intercity lanes between launch cities with sample prices per speed tier
        and next shared departures
      operationId: listIntercityLanes
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/service-areas:
    get:
      tags:
        - Discovery
      summary: List launch cities
      operationId: listServiceAreas
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/coverage:
    get:
      tags:
        - Discovery
      summary: Is an address/point covered?
      operationId: checkCoverage
      parameters:
        - name: address
          in: query
          schema:
            type: string
        - name: lat
          in: query
          schema:
            type: number
        - name: lng
          in: query
          schema:
            type: number
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  covered:
                    type: boolean
                  area:
                    type: object
                  nearest_area:
                    type: object
  /v1/quotes:
    post:
      tags:
        - Quotes
      summary: Price a delivery (no auth needed)
      operationId: getQuote
      requestBody:
        required: true
        content:
          application/json:
            schema: *a3
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  quote:
                    type: object
                    properties:
                      id:
                        type: string
                      client_price_cents:
                        type: integer
                      driver_pay_cents:
                        type: integer
                      currency:
                        type: string
                      eta_pickup_min:
                        type: integer
                      eta_dropoff_min:
                        type: integer
                      expires_at:
                        type: string
                      breakdown:
                        type: object
        "422":
          description: outside_coverage / prohibited_item
  /v1/stats:
    get:
      tags:
        - Discovery
      summary: Live platform stats
      operationId: getStats
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/signup/verify:
    get:
      tags:
        - Account
      summary: Verify email from the link sent at signup; unlocks the welcome credit
      operationId: verifySignup
      parameters:
        - name: token
          in: query
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/signup:
    post:
      tags:
        - Account
      summary: Create an account + API key (shown once); email verification unlocks
        the $20 credit
      operationId: signup
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - email
              properties:
                name:
                  type: string
                email:
                  type: string
                  format: email
                kind:
                  type: string
                  enum:
                    - agent
                    - app
                    - business
                agent_framework:
                  type: string
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  api_key:
                    type: string
                  organization_id:
                    type: string
                  wallet_balance_cents:
                    type: integer
  /v1/deliveries:
    post:
      tags:
        - Deliveries
      security: &a6
        - ApiKeyAuth: []
      summary: Create (and fund) a delivery
      operationId: createDelivery
      requestBody:
        required: true
        content:
          application/json:
            schema: *a4
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  delivery: *a5
        "402":
          description: insufficient_funds — deposit first
        "403":
          description: spending_cap
        "422":
          description: outside_coverage / prohibited_item
    get:
      tags:
        - Deliveries
      security: *a6
      summary: List deliveries
      operationId: listDeliveries
      parameters:
        - name: status
          in: query
          schema:
            type: string
          description: "'active' or comma list"
        - name: since
          in: query
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          schema:
            type: integer
            default: 25
        - name: offset
          in: query
          schema:
            type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  deliveries:
                    type: array
                    items: *a5
                  total:
                    type: integer
  /v1/deliveries/batch:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Create up to 25 deliveries
      operationId: createBatch
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                deliveries:
                  type: array
                  items: *a4
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}:
    get:
      tags:
        - Deliveries
      security: *a6
      summary: Get a delivery (id or short code)
      operationId: getDelivery
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  delivery: *a5
    patch:
      tags:
        - Deliveries
      security: *a6
      summary: Update instructions / contingency / drop-off contact
      operationId: updateDelivery
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                instructions:
                  type: string
                contingency:
                  type: object
                dropoff_contact:
                  type: object
                window_end:
                  type: string
                metadata:
                  type: object
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/fund:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Fund from wallet and start dispatch
      operationId: fundDelivery
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
        "402":
          description: insufficient_funds
  /v1/deliveries/{id}/cancel:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Cancel (free before a driver is assigned; 30% driver fee after)
      operationId: cancelDelivery
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
        "409":
          description: invalid_state (already picked up)
  /v1/deliveries/{id}/confirm:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Confirm a delivered job and release payment (auto after 24h)
      operationId: confirmDelivery
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/tip:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Tip the driver
      operationId: tipDriver
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - amount_cents
              properties:
                amount_cents:
                  type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/rate:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Rate the driver 1–5
      operationId: rateDriver
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - stars
              properties:
                stars:
                  type: integer
                comment:
                  type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/dispute:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Open a dispute (funds stay held)
      operationId: openDispute
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - reason
              properties:
                reason:
                  type: string
                evidence:
                  type: array
                  items:
                    type: string
                    format: uri
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/return:
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Create the reverse leg (return to pickup)
      operationId: createReturn
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/track:
    get:
      tags:
        - Tracking
      security: *a6
      summary: Live status, ETA, driver position
      operationId: trackDelivery
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/events:
    get:
      tags:
        - Tracking
      security: *a6
      summary: Event timeline; add ?stream=1 for SSE
      operationId: deliveryEvents
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: stream
          in: query
          schema:
            type: string
            enum:
              - "1"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/proof:
    get:
      tags:
        - Tracking
      security: *a6
      summary: Proof of delivery (signed photo URLs, signature, recipient)
      operationId: getProof
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/deliveries/{id}/messages:
    get:
      tags:
        - Deliveries
      security: *a6
      summary: Messages with the driver
      operationId: getMessages
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
    post:
      tags:
        - Deliveries
      security: *a6
      summary: Message the driver
      operationId: sendMessage
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - body
              properties:
                body:
                  type: string
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/account:
    get:
      tags:
        - Account
      security: *a6
      summary: Account, balance, capabilities
      operationId: getAccount
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/account/controls:
    patch:
      tags:
        - Account
      security: *a6
      summary: Spending caps
      operationId: setSpendingControls
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                spending_cap_per_delivery_cents:
                  type: integer
                  nullable: true
                daily_cap_cents:
                  type: integer
                  nullable: true
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/wallet:
    get:
      tags:
        - Wallet
      security: *a6
      summary: Balance + recent ledger
      operationId: getWallet
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/wallet/deposit:
    post:
      tags:
        - Wallet
      security: *a6
      summary: Card deposit via Stripe Checkout (returns a URL for a human to pay)
      operationId: createDeposit
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - amount_cents
              properties:
                amount_cents:
                  type: integer
                  minimum: 500
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  checkout_url:
                    type: string
        "501":
          description: deposits_not_configured
  /v1/x402/wallet/deposit:
    post:
      tags:
        - Wallet
      security: *a6
      summary: USDC on Base via x402 (HTTP 402 flow; no human needed)
      operationId: x402Deposit
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                amountCents:
                  type: integer
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
        "402":
          description: Payment requirements (x402 v2)
        "404":
          description: x402_not_enrolled
  /v1/api-keys:
    get:
      tags:
        - Account
      security: *a6
      summary: List keys
      operationId: listApiKeys
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
    post:
      tags:
        - Account
      security: *a6
      summary: Create key (max 10)
      operationId: createApiKey
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/api-keys/{id}:
    delete:
      tags:
        - Account
      security: *a6
      summary: Revoke key
      operationId: revokeApiKey
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/webhooks:
    get:
      tags:
        - Webhooks
      security: *a6
      summary: List webhooks
      operationId: listWebhooks
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
    post:
      tags:
        - Webhooks
      security: *a6
      summary: Register a webhook (HMAC-signed)
      operationId: createWebhook
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - url
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/webhooks/{id}:
    delete:
      tags:
        - Webhooks
      security: *a6
      summary: Remove webhook
      operationId: deleteWebhook
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/events/stream:
    get:
      tags:
        - Tracking
      security: *a6
      summary: SSE feed of every event on your account
      operationId: accountStream
      responses:
        "200":
          description: text/event-stream
  /v1/shopify/shops:
    get:
      tags:
        - Shopify
      security: *a6
      summary: Shopify shops connected to this account
      operationId: listShopifyShops
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  shops:
                    type: array
                    items:
                      type: object
  /v1/shopify/orders:
    get:
      tags:
        - Shopify
      security: *a6
      summary: Shopify orders seen by the app with their delivery status
      operationId: listShopifyOrders
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - skipped
              - booked
              - needs_funds
              - failed
              - cancelled
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
  /v1/shopify/orders/{ref}/book:
    post:
      tags:
        - Shopify
      security: *a6
      summary: "Book a delivery for a Shopify order (by order name like #1001, order
        id, or our row id); fetches the order from Shopify if needed"
      operationId: bookShopifyOrder
      parameters:
        - name: ref
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                force:
                  type: boolean
                  description: Ignore the shop's radius / size limits
                shop:
                  type: string
                  description: myshopify domain when several shops are connected
      responses:
        "200":
          description: Not booked — see order.status (skipped / needs_funds / failed) and
            order.last_error
        "201":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  order:
                    type: object
                  delivery: *a5
  /v1/track/{code}:
    get:
      tags:
        - Tracking
      summary: Public recipient tracking by short code
      operationId: publicTrack
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
