{
 "openapi": "3.0.3",
 "info": {
  "title": "MotorHangar Inventory API",
  "version": "1.0.0",
  "description": "Publish, update and mark as sold your dealership's cars on MotorHangar, and read the buyers who contacted you. Free. Authenticate with `Authorization: Bearer <key>`; create keys in your dealer panel. Full guide: https://motorhangar.com/developers/",
  "contact": {
   "name": "MotorHangar",
   "url": "https://motorhangar.com/developers/"
  }
 },
 "servers": [
  {
   "url": "https://motorhangar.com/api/v1"
  }
 ],
 "tags": [
  {
   "name": "Vehicles",
   "description": "Your stock, identified by your own stock ID."
  },
  {
   "name": "Sync",
   "description": "Send a whole stock and mark the missing cars as sold."
  },
  {
   "name": "Catalog",
   "description": "Brands, models and what each market accepts."
  },
  {
   "name": "Leads",
   "description": "Buyers who contacted you about your cars."
  },
  {
   "name": "Webhooks",
   "description": "We call your URL when something happens. See /developers/#webhooks."
  },
  {
   "name": "Account",
   "description": "Your key and your dealership."
  }
 ],
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "Your API key (`mh_\u2026`)."
   },
   "apiKey": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key"
   }
  },
  "schemas": {
   "VehicleInput": {
    "type": "object",
    "required": [
     "brand",
     "model",
     "year",
     "price",
     "mileage",
     "photos"
    ],
    "properties": {
     "brand": {
      "type": "string",
      "example": "Toyota",
      "description": "Name or slug from `GET /catalog/brands`. Unknown brands are rejected."
     },
     "model": {
      "type": "string",
      "example": "RAV4",
      "description": "Name or slug from `GET /catalog/brands/{slug}/models`. The base model, not the trim."
     },
     "year": {
      "type": "integer",
      "example": 2019,
      "minimum": 1950
     },
     "version": {
      "type": "string",
      "example": "XLE AWD",
      "maxLength": 160,
      "description": "Trim or version, free text."
     },
     "price": {
      "type": "number",
      "example": 18500,
      "exclusiveMinimum": true,
      "minimum": 0
     },
     "currency": {
      "type": "string",
      "example": "USD",
      "description": "One of the market's `currencies` (`GET /markets/{code}`). Defaults to the market's own."
     },
     "mileage": {
      "type": "integer",
      "example": 45000,
      "minimum": 0,
      "description": "In the market's `mileage_unit`: miles in the US and UK, km elsewhere. 0 for a new car."
     },
     "fuel": {
      "type": "string",
      "example": "petrol",
      "description": "One of the market's `fuels`."
     },
     "transmission": {
      "type": "string",
      "enum": [
       "manual",
       "automatic"
      ]
     },
     "color": {
      "type": "string",
      "example": "White"
     },
     "condition": {
      "type": "string",
      "enum": [
       "used",
       "new"
      ],
      "default": "used"
     },
     "city": {
      "type": "string",
      "example": "Lima",
      "description": "City name or slug in the market. Defaults to your dealership's city."
     },
     "description": {
      "type": "string",
      "maxLength": 2000
     },
     "photos": {
      "type": "array",
      "minItems": 3,
      "maxItems": 10,
      "items": {
       "type": "string",
       "format": "uri"
      },
      "example": [
       "https://example.com/1.jpg",
       "https://example.com/2.jpg",
       "https://example.com/3.jpg"
      ],
      "description": "3 to 10 public image URLs. We download them within minutes and the car goes live when they arrive. Optional on updates if the car already has its photos."
     },
     "market": {
      "type": "string",
      "example": "pe",
      "description": "Required only if your account sells in more than one market."
     },
     "source": {
      "type": "string",
      "enum": [
       "api",
       "woocommerce",
       "wordpress",
       "shopify",
       "prestashop",
       "odoo",
       "tiendanube",
       "n8n",
       "zapier",
       "make",
       "feed"
      ],
      "default": "api",
      "description": "Where this car comes from. Marking missing cars as sold only touches cars of the same source."
     }
    }
   },
   "Vehicle": {
    "type": "object",
    "properties": {
     "external_id": {
      "type": "string",
      "description": "Your stock ID."
     },
     "listing_id": {
      "type": "string",
      "description": "MotorHangar's listing code."
     },
     "market": {
      "type": "string"
     },
     "status": {
      "type": "string",
      "enum": [
       "active",
       "in_review",
       "importing_photos",
       "sold",
       "expired",
       "withdrawn"
      ]
     },
     "source": {
      "type": "string"
     },
     "url": {
      "type": "string",
      "format": "uri",
      "description": "Public listing page."
     },
     "brand": {
      "type": "string"
     },
     "model": {
      "type": "string"
     },
     "year": {
      "type": "integer"
     },
     "version": {
      "type": "string"
     },
     "price": {
      "type": "string",
      "nullable": true,
      "description": "Decimal as a string, so no precision is lost."
     },
     "currency": {
      "type": "string"
     },
     "mileage": {
      "type": "integer",
      "nullable": true
     },
     "mileage_unit": {
      "type": "string",
      "enum": [
       "km",
       "mi"
      ]
     },
     "fuel": {
      "type": "string"
     },
     "transmission": {
      "type": "string"
     },
     "color": {
      "type": "string"
     },
     "condition": {
      "type": "string"
     },
     "city": {
      "type": "string",
      "nullable": true
     },
     "description": {
      "type": "string"
     },
     "photos": {
      "type": "array",
      "items": {
       "type": "string",
       "format": "uri"
      }
     },
     "photos_pending": {
      "type": "integer",
      "description": "Photos still being downloaded."
     },
     "updated_at": {
      "type": "string",
      "format": "date-time",
      "nullable": true
     }
    }
   },
   "VehicleResult": {
    "type": "object",
    "properties": {
     "result": {
      "type": "string",
      "enum": [
       "created",
       "updated",
       "withdrawn",
       "sold"
      ]
     },
     "vehicle": {
      "$ref": "#/components/schemas/Vehicle"
     }
    }
   },
   "VehicleList": {
    "type": "object",
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Vehicle"
      }
     },
     "page": {
      "type": "integer"
     },
     "per_page": {
      "type": "integer"
     },
     "total": {
      "type": "integer"
     }
    }
   },
   "SyncRequest": {
    "type": "object",
    "required": [
     "vehicles"
    ],
    "properties": {
     "market": {
      "type": "string"
     },
     "source": {
      "type": "string",
      "enum": [
       "api",
       "woocommerce",
       "wordpress",
       "shopify",
       "prestashop",
       "odoo",
       "tiendanube",
       "n8n",
       "zapier",
       "make",
       "feed"
      ],
      "default": "api"
     },
     "mark_missing_as_sold": {
      "type": "boolean",
      "default": false,
      "description": "Mark as sold the cars of this source that are not in the list. Skipped if any vehicle fails. Only for stocks that fit in one call."
     },
     "allow_mass_sold": {
      "type": "boolean",
      "default": false,
      "description": "Override the guard against marking too many cars as sold at once."
     },
     "vehicles": {
      "type": "array",
      "maxItems": 500,
      "items": {
       "allOf": [
        {
         "$ref": "#/components/schemas/VehicleInput"
        },
        {
         "type": "object",
         "required": [
          "external_id"
         ],
         "properties": {
          "external_id": {
           "type": "string"
          }
         }
        }
       ]
      }
     }
    }
   },
   "SyncResult": {
    "type": "object",
    "properties": {
     "created": {
      "type": "integer"
     },
     "updated": {
      "type": "integer"
     },
     "sold": {
      "type": "integer"
     },
     "sold_skipped": {
      "type": "string",
      "description": "Why no car was marked as sold, when that happens."
     },
     "errors": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "index": {
         "type": "integer"
        },
        "external_id": {
         "type": "string"
        },
        "message": {
         "type": "string"
        }
       }
      }
     }
    }
   },
   "ReconcileRequest": {
    "type": "object",
    "required": [
     "external_ids"
    ],
    "properties": {
     "market": {
      "type": "string"
     },
     "source": {
      "type": "string",
      "enum": [
       "api",
       "woocommerce",
       "wordpress",
       "shopify",
       "prestashop",
       "odoo",
       "tiendanube",
       "n8n",
       "zapier",
       "make",
       "feed"
      ],
      "default": "api"
     },
     "external_ids": {
      "type": "array",
      "maxItems": 20000,
      "items": {
       "type": "string"
      },
      "description": "EVERY stock ID of this source that is still for sale."
     },
     "allow_mass_sold": {
      "type": "boolean",
      "default": false
     }
    }
   },
   "ReconcileResult": {
    "type": "object",
    "properties": {
     "result": {
      "type": "string",
      "enum": [
       "reconciled"
      ]
     },
     "sold": {
      "type": "integer"
     },
     "kept": {
      "type": "integer"
     }
    }
   },
   "Account": {
    "type": "object",
    "properties": {
     "dealer": {
      "type": "object",
      "properties": {
       "name": {
        "type": "string"
       },
       "slug": {
        "type": "string"
       },
       "markets": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      }
     },
     "key": {
      "type": "object",
      "properties": {
       "prefix": {
        "type": "string"
       },
       "name": {
        "type": "string"
       }
      }
     },
     "limits": {
      "type": "object",
      "properties": {
       "per_minute": {
        "type": "integer"
       },
       "sync_max": {
        "type": "integer"
       },
       "min_photos": {
        "type": "integer"
       }
      }
     },
     "api_version": {
      "type": "string"
     }
    }
   },
   "Market": {
    "type": "object",
    "properties": {
     "code": {
      "type": "string",
      "example": "pe"
     },
     "name": {
      "type": "string",
      "example": "Peru"
     },
     "currencies": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "example": [
       "PEN",
       "USD"
      ]
     },
     "default_currency": {
      "type": "string"
     },
     "mileage_unit": {
      "type": "string",
      "enum": [
       "km",
       "mi"
      ]
     },
     "fuels": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "transmissions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "conditions": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "photos": {
      "type": "object",
      "properties": {
       "min": {
        "type": "integer"
       },
       "max": {
        "type": "integer"
       }
      }
     }
    }
   },
   "BrandList": {
    "type": "object",
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "slug": {
         "type": "string"
        }
       }
      }
     }
    }
   },
   "ModelList": {
    "type": "object",
    "properties": {
     "brand": {
      "type": "string"
     },
     "data": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "slug": {
         "type": "string"
        },
        "years": {
         "type": "array",
         "items": {
          "type": "integer"
         }
        }
       }
      }
     }
    }
   },
   "LeadList": {
    "type": "object",
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "id": {
         "type": "integer"
        },
        "external_id": {
         "type": "string"
        },
        "listing_id": {
         "type": "string"
        },
        "stage": {
         "type": "string"
        },
        "created_at": {
         "type": "string",
         "format": "date-time"
        },
        "messages": {
         "type": "integer"
        },
        "last_message": {
         "type": "string",
         "nullable": true
        },
        "url": {
         "type": "string",
         "format": "uri"
        }
       }
      }
     }
    }
   },
   "WebhookInput": {
    "type": "object",
    "required": [
     "url"
    ],
    "properties": {
     "url": {
      "type": "string",
      "format": "uri",
      "description": "https only."
     },
     "events": {
      "type": "array",
      "items": {
       "type": "string",
       "enum": [
        "lead.created",
        "vehicle.sold",
        "vehicle.rejected",
        "sync.failed"
       ]
      },
      "description": "Defaults to all."
     }
    }
   },
   "Webhook": {
    "type": "object",
    "properties": {
     "id": {
      "type": "integer"
     },
     "url": {
      "type": "string"
     },
     "events": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "active": {
      "type": "boolean"
     },
     "created_at": {
      "type": "string",
      "format": "date-time"
     },
     "secret": {
      "type": "string",
      "description": "Only in the response that creates it. Signs every delivery."
     }
    }
   },
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string",
        "example": "invalid_vehicle"
       },
       "message": {
        "type": "string"
       },
       "fields": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      }
     }
    }
   }
  },
  "responses": {
   "BadRequest": {
    "description": "The request is not valid (market, source, JSON\u2026).",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "Unauthorized": {
    "description": "Missing, invalid or revoked API key.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "NotFound": {
    "description": "No such vehicle, brand or market.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "RateLimited": {
    "description": "More than 120 calls in a minute. Wait `Retry-After` seconds.",
    "headers": {
     "Retry-After": {
      "schema": {
       "type": "integer"
      }
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "Invalid": {
    "description": "The vehicle breaks a quality rule: `fields` lists which.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  }
 },
 "security": [
  {
   "bearer": []
  },
  {
   "apiKey": []
  }
 ],
 "paths": {
  "/me": {
   "get": {
    "operationId": "getAccount",
    "tags": [
     "Account"
    ],
    "summary": "Your dealership, markets and limits",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Account"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/markets/{code}": {
   "get": {
    "operationId": "getMarket",
    "tags": [
     "Catalog"
    ],
    "summary": "What a market accepts: currencies, fuels, mileage unit",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Market"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "parameters": [
     {
      "name": "code",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "example": "pe"
     }
    ]
   }
  },
  "/vehicles": {
   "get": {
    "operationId": "listVehicles",
    "tags": [
     "Vehicles"
    ],
    "summary": "List your vehicles",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/VehicleList"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "description": "Newest updates first. Use `updated_since` to poll for changes.",
    "parameters": [
     {
      "name": "market",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Only if your account sells in more than one market."
     },
     {
      "name": "source",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "api",
        "woocommerce",
        "wordpress",
        "shopify",
        "prestashop",
        "odoo",
        "tiendanube",
        "n8n",
        "zapier",
        "make",
        "feed"
       ]
      }
     },
     {
      "name": "updated_since",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "page",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 1
      }
     },
     {
      "name": "per_page",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 50,
       "maximum": 100
      }
     }
    ]
   }
  },
  "/vehicles/{external_id}": {
   "get": {
    "operationId": "getVehicle",
    "tags": [
     "Vehicles"
    ],
    "summary": "Get one vehicle",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "vehicle": {
           "$ref": "#/components/schemas/Vehicle"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "parameters": [
     {
      "name": "external_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "maxLength": 80
      },
      "description": "Your own stock ID. The same ID always means the same car."
     },
     {
      "name": "market",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Only if your account sells in more than one market."
     }
    ]
   },
   "put": {
    "operationId": "upsertVehicle",
    "tags": [
     "Vehicles"
    ],
    "summary": "Create or update a vehicle",
    "responses": {
     "200": {
      "description": "Updated",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/VehicleResult"
        }
       }
      }
     },
     "201": {
      "description": "Created",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/VehicleResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "422": {
      "$ref": "#/components/responses/Invalid"
     }
    },
    "description": "Idempotent: sending the same stock ID again updates the car instead of duplicating it. A sold car sent again is back for sale.",
    "parameters": [
     {
      "name": "external_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "maxLength": 80
      },
      "description": "Your own stock ID. The same ID always means the same car."
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/VehicleInput"
       }
      }
     }
    }
   },
   "delete": {
    "operationId": "withdrawVehicle",
    "tags": [
     "Vehicles"
    ],
    "summary": "Withdraw a vehicle",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/VehicleResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "description": "It stops being published. Nothing is deleted: its page keeps answering, as a sold car's does.",
    "parameters": [
     {
      "name": "external_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "maxLength": 80
      },
      "description": "Your own stock ID. The same ID always means the same car."
     },
     {
      "name": "market",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Only if your account sells in more than one market."
     }
    ]
   }
  },
  "/vehicles/{external_id}/sold": {
   "post": {
    "operationId": "markVehicleSold",
    "tags": [
     "Vehicles"
    ],
    "summary": "Mark a vehicle as sold",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/VehicleResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "parameters": [
     {
      "name": "external_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "maxLength": 80
      },
      "description": "Your own stock ID. The same ID always means the same car."
     },
     {
      "name": "market",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "Only if your account sells in more than one market."
     }
    ]
   }
  },
  "/vehicles/sync": {
   "post": {
    "operationId": "syncVehicles",
    "tags": [
     "Sync"
    ],
    "summary": "Create or update up to 500 vehicles in one call",
    "responses": {
     "200": {
      "description": "All applied",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/SyncResult"
        }
       }
      }
     },
     "207": {
      "description": "Some failed: see `errors`. The others were applied.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/SyncResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "413": {
      "description": "More than 500 vehicles.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "description": "Each vehicle is validated and applied on its own: one bad car never blocks the rest. For more than 500 cars, send several calls without `mark_missing_as_sold` and finish with `POST /vehicles/reconcile`.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SyncRequest"
       }
      }
     }
    }
   }
  },
  "/vehicles/reconcile": {
   "post": {
    "operationId": "reconcileVehicles",
    "tags": [
     "Sync"
    ],
    "summary": "Mark as sold the cars of a source that are no longer in your stock",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ReconcileResult"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "409": {
      "description": "Refused by the guard (`too_many_missing` or `empty_list`).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "description": "Send every stock ID of the source that is still for sale. The others of that source are marked as sold. Guard: if that would mark more than 40% of the source's cars at once (and at least 5), nothing is marked and you get 409 \u2014 usually a partial export. An empty list is always refused.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ReconcileRequest"
       }
      }
     }
    }
   }
  },
  "/catalog/brands": {
   "get": {
    "operationId": "listBrands",
    "tags": [
     "Catalog"
    ],
    "summary": "Brands in the catalog",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/BrandList"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   }
  },
  "/catalog/brands/{slug}/models": {
   "get": {
    "operationId": "listModels",
    "tags": [
     "Catalog"
    ],
    "summary": "Models and years of a brand",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/ModelList"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "parameters": [
     {
      "name": "slug",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "example": "toyota"
     }
    ]
   }
  },
  "/leads": {
   "get": {
    "operationId": "listLeads",
    "tags": [
     "Leads"
    ],
    "summary": "Buyers who wrote about your cars",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LeadList"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "description": "Newest first, up to 100. Use `since` to poll. The buyer's contact details are not included: they are shared in the conversation when the buyer chooses to.",
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     }
    ]
   }
  },
  "/webhooks": {
   "get": {
    "operationId": "listWebhooks",
    "tags": [
     "Webhooks"
    ],
    "summary": "Your webhooks and the events you can subscribe to",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "data": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Webhook"
           }
          },
          "events": {
           "type": "array",
           "items": {
            "type": "string"
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    }
   },
   "post": {
    "operationId": "createWebhook",
    "tags": [
     "Webhooks"
    ],
    "summary": "Subscribe a URL to events",
    "responses": {
     "201": {
      "description": "Created. `secret` is returned only here.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Webhook"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     }
    },
    "description": "For n8n, Make or Zapier triggers that register themselves. Up to 20 per account.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/WebhookInput"
       }
      }
     }
    }
   }
  },
  "/webhooks/{id}": {
   "get": {
    "operationId": "getWebhook",
    "tags": [
     "Webhooks"
    ],
    "summary": "One webhook",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Webhook"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ]
   },
   "delete": {
    "operationId": "deleteWebhook",
    "tags": [
     "Webhooks"
    ],
    "summary": "Unsubscribe",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/RateLimited"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     }
    },
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ]
   }
  },
  "/feeds/{token}/": {
   "post": {
    "operationId": "pushFeed",
    "tags": [
     "Sync"
    ],
    "security": [],
    "summary": "Push your whole inventory file (for providers)",
    "description": "For your website company, DMS or inventory provider: POST the WHOLE inventory file (CSV, Excel, JSON or XML, up to 10 MB) to the private address shown in the dealer panel (Inventory feed \u2192 My provider will send it). No API key: the token in the address is the secret. Same rules as /vehicles/sync, including the sold guard.",
    "parameters": [
     {
      "name": "token",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "text/csv": {
       "schema": {
        "type": "string"
       }
      },
      "application/json": {
       "schema": {
        "type": "object"
       }
      },
      "application/xml": {
       "schema": {
        "type": "string"
       }
      },
      "multipart/form-data": {
       "schema": {
        "type": "object",
        "properties": {
         "file": {
          "type": "string",
          "format": "binary"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Processed"
     },
     "404": {
      "$ref": "#/components/responses/NotFound"
     },
     "413": {
      "description": "Larger than 10 MB",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "422": {
      "description": "The file could not be read",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  }
 }
}