{"openapi":"3.1.0","info":{"title":"Australian vehicle recalls — public API","version":"1.0.0","description":"Look up Australian vehicle recalls by VIN, search recalls, and manage watchlists. A VIN answers `recalled` with matches or `no_record`: the mirror never claims a vehicle is free of recalls."},"servers":[{"url":"https://autozoo.com.au"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"The `rk_…` key returned once by `POST /v1/accounts`."}},"schemas":{"LookupOutcome":{"anyOf":[{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"vin":{"type":"string"},"status":{"type":"string","enum":["recalled","no_record"]},"matches":{"type":"array","items":{"$ref":"#/components/schemas/RecallMatch"}},"disclaimer":{"type":"string"}},"required":["ok","vin","status"]},{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"vin":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string","enum":["invalid_vin"]},"message":{"type":"string"}},"required":["code","message"]}},"required":["ok","vin","error"]}]},"RecallMatch":{"type":"object","properties":{"recall_number":{"type":"string"},"make":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"hazard":{"type":"string","nullable":true},"published_at":{"type":"string","nullable":true},"source_url":{"type":"string"},"basis":{"type":"string","enum":["vin_list","vin_range_exact","vin_range_envelope"]}},"required":["recall_number","make","model","hazard","published_at","source_url","basis"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"]},"LookupResult":{"anyOf":[{"type":"object","properties":{"vin":{"type":"string"},"status":{"type":"string","enum":["recalled"]},"matches":{"type":"array","items":{"$ref":"#/components/schemas/RecallMatch"}}},"required":["vin","status","matches"]},{"type":"object","properties":{"vin":{"type":"string"},"status":{"type":"string","enum":["no_record"]},"disclaimer":{"type":"string"}},"required":["vin","status","disclaimer"]}]},"RecallSummary":{"type":"object","properties":{"recall_number":{"type":"string"},"make":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"year_from":{"type":"integer","nullable":true},"year_to":{"type":"integer","nullable":true},"hazard":{"type":"string","nullable":true},"published_at":{"type":"string","nullable":true},"source_url":{"type":"string"}},"required":["recall_number","make","model","year_from","year_to","hazard","published_at","source_url"]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["operations"],"summary":"Liveness","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]}},"required":["status"]}}}}}}},"/v1/health":{"get":{"tags":["operations"],"summary":"Liveness (API namespace)","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]}},"required":["status"]}}}}}}},"/v1/readiness":{"get":{"tags":["operations"],"summary":"Readiness: is ingestion still inside its freshness windows","description":"Answers 503 when the last completed full crawl is older than nine days or the last completed delta is older than three hours. The two clocks fail independently, because a healthy feed over a stale corpus is the state that used to look fresh.","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]},"as_of":{"type":"string"},"db_ms":{"type":"number"},"data_through":{"type":"string","nullable":true},"data_age_seconds":{"type":"number","nullable":true},"delta_through":{"type":"string","nullable":true},"delta_age_seconds":{"type":"number","nullable":true},"reasons":{"type":"array","items":{"type":"string"}}},"required":["ok","as_of","db_ms","data_through","data_age_seconds","delta_through","delta_age_seconds","reasons"]}}}},"503":{"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"as_of":{"type":"string"},"db_ms":{"type":"number"},"data_through":{"type":"string","nullable":true},"data_age_seconds":{"type":"number","nullable":true},"delta_through":{"type":"string","nullable":true},"delta_age_seconds":{"type":"number","nullable":true},"reasons":{"type":"array","items":{"type":"string"}}},"required":["ok","as_of","db_ms","data_through","data_age_seconds","delta_through","delta_age_seconds","reasons"]}}}}}}},"/v1/lookup":{"post":{"tags":["lookup"],"summary":"Look up several VINs","description":"One result per input in input order. An invalid VIN answers in place; it never fails the batch. Capped by MAX_LOOKUP_VINS (default 50).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"vins":{"type":"array","items":{"type":"string"}}},"required":["vins"]}}}},"responses":{"200":{"description":"Per-VIN results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/LookupOutcome"}}},"required":["results"]}}}},"400":{"description":"Malformed body, or a batch above the limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/vins/{vin}":{"get":{"tags":["lookup"],"summary":"Recalls for one VIN","parameters":[{"schema":{"type":"string","description":"17-character VIN; case-insensitive.","example":"6K9CAVECXSM804000"},"required":true,"description":"17-character VIN; case-insensitive.","name":"vin","in":"path"}],"responses":{"200":{"description":"Recalled, or a stated no-record answer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResult"}}}},"400":{"description":"Not a VIN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/makes":{"get":{"tags":["recalls"],"summary":"Makes with active recalls","description":"Every make with at least one active recall, and its recall count. Cached for five minutes; changes only when the corpus does.","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"makes":{"type":"array","items":{"type":"object","properties":{"make":{"type":"string"},"recalls":{"type":"integer"}},"required":["make","recalls"]}}},"required":["makes"]}}}}}}},"/v1/makes/{make}":{"get":{"tags":["recalls"],"summary":"Models for one make","description":"The model names under a make, with recall counts. The make match is case-insensitive. 404 when the make has no active recalls.","parameters":[{"schema":{"type":"string"},"required":true,"name":"make","in":"path"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"make":{"type":"string"},"models":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string"},"recalls":{"type":"integer"}},"required":["model","recalls"]}}},"required":["make","models"]}}}},"404":{"description":"No recalls for that make","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/recalls":{"get":{"tags":["recalls"],"summary":"Search recalls","parameters":[{"schema":{"type":"string"},"required":false,"name":"make","in":"query"},{"schema":{"type":"string"},"required":false,"name":"model","in":"query"},{"schema":{"type":"string","description":"Four-digit year."},"required":false,"description":"Four-digit year.","name":"year","in":"query"},{"schema":{"type":"string","format":"date"},"required":false,"name":"since","in":"query"},{"schema":{"type":"string","description":"Clamped to MAX_SEARCH_LIMIT (default 100); default page 20."},"required":false,"description":"Clamped to MAX_SEARCH_LIMIT (default 100); default page 20.","name":"limit","in":"query"},{"schema":{"type":"string","description":"The `next` value from the previous page."},"required":false,"description":"The `next` value from the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page ordered by publication date","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RecallSummary"}},"next":{"type":"string","nullable":true}},"required":["items","next"]}}}},"400":{"description":"Invalid query value","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/recalls/{recallNumber}":{"get":{"tags":["recalls"],"summary":"One recall with its files and VIN basis","parameters":[{"schema":{"type":"string"},"required":true,"name":"recallNumber","in":"path"}],"responses":{"200":{"description":"Recall detail"},"404":{"description":"No such recall","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/coverage":{"get":{"tags":["operations"],"summary":"How many recalls have a usable VIN list","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"recalls_total":{"type":"integer"},"recalls_checked":{"type":"integer"},"by_reason":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["recalls_total","recalls_checked","by_reason"]}}}}}}},"/v1/stats":{"get":{"tags":["operations"],"summary":"Corpus and ingest counts","responses":{"200":{"description":"Counts"}}}},"/v1/status":{"get":{"tags":["operations"],"summary":"Freshness of the crawl and the feed, and the enforced limits","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"as_of":{"type":"string"},"data_through":{"type":"string","nullable":true},"data_age_seconds":{"type":"integer"},"delta_through":{"type":"string","nullable":true},"delta_age_seconds":{"type":"integer"},"reasons":{"type":"array","items":{"type":"string"}},"limits":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["ok","as_of","data_through","data_age_seconds","delta_through","delta_age_seconds","reasons","limits"]}}}}}}},"/v1/accounts":{"post":{"tags":["watchlists"],"summary":"Create an account and receive its API key once","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"]}}}},"responses":{"201":{"description":"Created; `api_key` is shown only here","content":{"application/json":{"schema":{"type":"object","properties":{"watcher_id":{"type":"integer"},"status":{"type":"string","enum":["pending","active"]},"api_key":{"type":"string"},"confirm_sent":{"type":"boolean"}},"required":["watcher_id","status","api_key","confirm_sent"]}}}},"400":{"description":"Invalid address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That address already has an account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channel":{"put":{"tags":["watchlists"],"summary":"Choose where notifications go (account-level)","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","enum":["email","webhook"]},"target":{"type":"string"}},"required":["channel"]}}}},"responses":{"200":{"description":"Channel set. A webhook response carries the secret once.","content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string"},"target":{"type":"string"},"secret":{"type":"string"}},"required":["channel"]}}}},"400":{"description":"Invalid channel or target","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or wrong API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/channel/test":{"post":{"tags":["watchlists"],"summary":"Send a signed webhook.test event to the configured target","security":[{"apiKey":[]}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"delivered":{"type":"boolean"},"status":{"type":"integer"}},"required":["delivered","status"]}}}},"400":{"description":"No webhook channel configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or wrong API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The endpoint refused the request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/watchlists":{"post":{"tags":["watchlists"],"summary":"Create a list","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"watchlist_id":{"type":"integer"},"name":{"type":"string"}},"required":["watchlist_id","name"]}}}},"400":{"description":"Invalid name, or too many lists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or wrong API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"That list name is already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["watchlists"],"summary":"The account's lists","security":[{"apiKey":[]}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"watchlists":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"created_at":{"type":"string"},"items":{"type":"integer"}},"required":["id","name","created_at","items"]}}},"required":["status","watchlists"]}}}},"401":{"description":"Missing or wrong API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/watchlists/{id}/items":{"get":{"tags":["watchlists"],"summary":"The VINs on one list","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"watchlist_id":{"type":"integer"},"name":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"vin":{"type":"string"},"created_at":{"type":"string"}},"required":["vin","created_at"]}}},"required":["watchlist_id","name","items"]}}}},"401":{"description":"Missing or wrong API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["watchlists"],"summary":"Add VINs to a list","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"vins":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["vins"]}}}},"responses":{"200":{"description":"Added; invalid VINs are reported, not rejected","content":{"application/json":{"schema":{"type":"object","properties":{"watchlist_id":{"type":"integer"},"added":{"type":"integer"},"invalid":{"type":"array","items":{"type":"object","properties":{"vin":{"type":"string"},"reason":{"type":"string"}},"required":["vin","reason"]}}},"required":["watchlist_id","added","invalid"]}}}},"400":{"description":"Invalid body, too many VINs, or the list is full","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or wrong API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/watchlists/{id}/items/{vin}":{"delete":{"tags":["watchlists"],"summary":"Remove one VIN from one list","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":true,"name":"vin","in":"path"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"watchlist_id":{"type":"integer"},"vin":{"type":"string"},"removed":{"type":"integer"}},"required":["watchlist_id","vin","removed"]}}}},"400":{"description":"Not a VIN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or wrong API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}