{
  "openapi": "3.0.3",
  "info": {
    "title": "DriveExpert API",
    "description": "Drive-completed webhooks for DriveExpert customers and approved partners. Interactive docs: https://developer.algodriven.xyz/driveexpert\n\nPartners do not list or create drives over this API. Subscribe to `ON_DRIVE_COMPLETED` via `/partner/webhooks` and receive an HTTPS POST when a test drive or loan-car booking finishes. Manage subscriptions with the same partner CRUD used by EvalExpert (`type: drive`).\n\n**Auth:** send your partner API key as the raw `Authorization` header (no `Bearer` prefix).\n\n**Accessing a dealer.** Two ways to run a request as a given rooftop:\n- **Legacy — dedicated API key.** Issue one key per rooftop. The key is bound to that dealer, so every call already runs as that rooftop. Do not send `X-Dealer-Id`.\n- **New — `X-Dealer-Id` header.** Issue one partner key that covers several rooftops. Send `X-Dealer-Id` with that rooftop's `uid` (not the numeric DealerID) on every request. Required when the key is not bound to a single dealer; ignored on a dedicated key. The key must be authorised for that rooftop.\n\n**Signature:** every delivery includes `X-AlgoDriven-Signature` and `X-AlgoDriven-Timestamp` (see `x-sandbox.webhook_signature`).",
    "version": "1.2.0",
    "contact": {
      "name": "AlgoDriven",
      "url": "https://algodriven.xyz/developers/driveexpert-api/"
    }
  },
  "servers": [
    {
      "url": "https://evalexpert.io/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Webhooks",
      "description": "Outbound HTTPS callbacks when a drive is completed. Manage subscriptions here."
    }
  ],
  "paths": {
    "/partner/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List drive webhooks",
        "description": "List webhook subscriptions for the rooftop on this partner key. Filter to `type: drive` in your client.",
        "operationId": "listDrivePartnerWebhooks",
        "parameters": [
          {
            "$ref": "#/components/parameters/XDealerId"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook list"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a drive webhook",
        "description": "Register an HTTPS POST receiver for drive-completed events.\n\nSet `type` to `drive` and `trigger_event` to `ON_DRIVE_COMPLETED`. The delivered body is the DriveCompletedEvent schema.\n\nOptional custom auth headers: `auth_header_name` / `auth_header_value`.",
        "operationId": "createDrivePartnerWebhook",
        "parameters": [
          {
            "$ref": "#/components/parameters/XDealerId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "trigger_event",
                  "webhook"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "drive"
                    ]
                  },
                  "trigger_event": {
                    "type": "string",
                    "enum": [
                      "ON_DRIVE_COMPLETED"
                    ]
                  },
                  "webhook": {
                    "type": "string",
                    "format": "uri",
                    "description": "HTTPS POST URL"
                  },
                  "auth_header_name": {
                    "type": "string"
                  },
                  "auth_header_value": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "type": "drive",
                "trigger_event": "ON_DRIVE_COMPLETED",
                "webhook": "https://example.com/drive-completed",
                "auth_header_name": "Authorization",
                "auth_header_value": "Bearer xyz"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created"
          }
        }
      }
    },
    "/partner/webhooks/{id}": {
      "put": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a drive webhook",
        "operationId": "updateDrivePartnerWebhook",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/XDealerId"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "eval",
                      "drive"
                    ]
                  },
                  "trigger_event": {
                    "type": "string"
                  },
                  "webhook": {
                    "type": "string",
                    "format": "uri"
                  },
                  "auth_header_name": {
                    "type": "string"
                  },
                  "auth_header_value": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook updated"
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a drive webhook",
        "operationId": "deleteDrivePartnerWebhook",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/XDealerId"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook deleted"
          },
          "404": {
            "description": "Webhook not found"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "XDealerId": {
        "name": "X-Dealer-Id",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Rooftop to act as. Value is the dealer's `uid` (not the numeric DealerID). Required on a multi-rooftop partner key; ignored when the key is already bound to a single dealer."
      }
    },
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Partner API key issued by AlgoDriven. Send the raw key (not Bearer)."
      }
    },
    "schemas": {
      "DriveCompletedEvent": {
        "type": "object",
        "description": "Body POSTed to your webhook URL when a drive completes.",
        "properties": {
          "event": {
            "type": "string",
            "example": "ON_DRIVE_COMPLETED"
          },
          "drive_id": {
            "type": "string"
          },
          "dealer_id": {
            "type": "integer",
            "nullable": true
          },
          "driveType": {
            "type": "string",
            "nullable": true
          },
          "driveStartDate": {
            "type": "string",
            "nullable": true,
            "description": "MySQL datetime (`YYYY-MM-DD HH:MM:SS`) in the process timezone when present. Not ISO-8601 and not UTC-suffixed."
          },
          "driveEndDate": {
            "type": "string",
            "nullable": true,
            "description": "MySQL datetime (`YYYY-MM-DD HH:MM:SS`) in the process timezone when present. Not ISO-8601 and not UTC-suffixed."
          },
          "username": {
            "type": "string",
            "nullable": true
          },
          "driver_count": {
            "type": "integer"
          },
          "vehicle": {
            "type": "object",
            "properties": {
              "year": {
                "type": "string",
                "nullable": true
              },
              "make": {
                "type": "string",
                "nullable": true
              },
              "model": {
                "type": "string",
                "nullable": true
              },
              "vin": {
                "type": "string",
                "nullable": true
              },
              "rego": {
                "type": "string",
                "nullable": true
              },
              "stock": {
                "type": "string",
                "nullable": true
              },
              "km": {
                "type": "integer",
                "nullable": true,
                "description": "Odometer as stored on the drive. May arrive as a numeric string depending on the source."
              },
              "description": {
                "type": "string",
                "nullable": true
              }
            }
          }
        },
        "example": {
          "event": "ON_DRIVE_COMPLETED",
          "drive_id": "abc123",
          "dealer_id": 1234,
          "driveType": "test_drive",
          "driveStartDate": "2026-09-04 10:00:00",
          "driveEndDate": "2026-09-04 10:25:00",
          "username": "Jane Smith",
          "driver_count": 1,
          "vehicle": {
            "year": "2022",
            "make": "Toyota",
            "model": "Camry",
            "vin": "4T1BZ1HK4KU025338",
            "rego": "ABC123",
            "stock": "S-1001",
            "km": 42000,
            "description": "2022 Toyota Camry"
          }
        }
      }
    }
  },
  "x-sandbox": {
    "webhook_signature": {
      "header": "X-AlgoDriven-Signature",
      "format": "sha256=<hex>",
      "signed_payload": "{timestamp}.{raw_json_body}",
      "timestamp_header": "X-AlgoDriven-Timestamp"
    }
  }
}
