AutoUncle Data Services is in beta. The API, MCP tools and agent skill may change often, including breaking changes, until version 1.0. Read the changelog before you upgrade an integration.
Refusals & missing mileage
A refusal means "this input cannot be valued as sent". It is recorded, free, and comes with machine-readable recovery steps. This guide covers the refusals you will meet, the explicit mileage opt-in and strict resolution.
What a refusal looks like
A refused valuation returns the normal valuation envelope with "status": "refused", an error object and the error's HTTP status (usually 422):
{
"request_id": "val_ff744d44049c52dd6cb9d22b13e45636",
"object": "valuation",
"country": "DK",
"mode": "test",
"status": "refused",
"error": {
"code": "missing_mileage",
"status": 422,
"detail": "Supply the actual mileage in vehicle.mileage, or explicitly set options.allow_mileage_estimation to true to use a fixed 96,000 km baseline assumption.",
"errors": [{ "pointer": "/vehicle/mileage", "message": "Actual mileage or explicit baseline permission is required." }],
"fix": [
"Supply vehicle.mileage with its value and unit.",
"If a fixed baseline assumption is acceptable, set options.allow_mileage_estimation to true. Strict resolution still requires actual mileage."
],
"retryable": false,
"billable": false
},
"notes": [],
"attributions": [],
"usage": { "billable": false, "credits_used": 0, "idempotent_replay": false, "mode": "test" }
}
Three things to rely on:
- It is free. Any credit reserved for the request is released.
usage.credits_usedis0. - It is recorded. It has a
request_id, appears in the console request log, and replays if you send the same idempotency key and body again. - It tells you what to fix.
error.errors[].pointerpoints into your request, anderror.fixlists the steps. Showfixto users or log it; match onerror.code.
To try again after fixing the input, send the corrected body with a new idempotency key. Reusing the old key with a different body returns 409 idempotency_conflict.
Common refusals
| Code | HTTP | Typical cause | Fix |
|---|---|---|---|
missing_mileage |
422 | No mileage, and no opt-in to the baseline | Send vehicle.mileage, or opt in (below) |
vehicle_not_identified |
422 | Make or model not recognized; or strict mode without a year | Send make and model, or catalogue IDs |
catalogue_id_conflict |
422 | Catalogue IDs from different branches of the hierarchy | Send one consistent path, or only the lowest level |
unknown_catalogue_id |
404 | An ID that does not exist (for example from an old cache) | Look the vehicle up again in the catalogue |
vehicle_out_of_range |
422 | Year or mileage outside the supported range | Check the year and the mileage unit |
unsupported_vehicle |
422 | A characteristic the market's model does not cover, such as flags.is_damaged |
Remove a flag only if it does not apply; otherwise the vehicle cannot be valued. See error.reason when present |
precision_below_minimum |
422 | The result would be below your options.min_precision |
Add the details in fix, or lower the minimum |
Refusals are never retryable unchanged: retryable is false. The error reference lists every code with its recovery steps.
Missing mileage
Mileage is one of the strongest price factors, so the service does not assume it unless you say so. Without vehicle.mileage, a request is refused with missing_mileage, without spending a credit. This also applies when you send only a description and the mileage cannot be read from it.
Send the actual mileage
Always the best option. Send the value with its unit, and do not convert it yourself:
{
"mileage": { "value": 9000, "unit": "mil" }
}
km is kilometres, mi is miles and mil is the Scandinavian mile of 10 km. The service converts to the market's own unit for valuing; the result always reports the odometer in kilometres, as vehicle.odometer.km.
Or opt in to the baseline
If you genuinely cannot get the mileage and an indicative figure is still useful, opt in explicitly:
curl https://data-services.autouncle.com/v1/valuations \
-H "Authorization: Bearer $AUTOUNCLE_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: valuation-mileage-baseline-001' \
-d @- <<'JSON'
{
"country": "DK",
"vehicle": {
"make": "Volkswagen",
"model": "Golf",
"model_year": 2020
},
"options": {
"allow_mileage_estimation": true
}
}
JSON
require "json"
require "net/http"
uri = URI("https://data-services.autouncle.com/v1/valuations")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("AUTOUNCLE_API_KEY")}"
request["Content-Type"] = "application/json"
request["Idempotency-Key"] = "valuation-mileage-baseline-001"
request.body = JSON.generate({
"country" => "DK",
"vehicle" => {
"make" => "Volkswagen",
"model" => "Golf",
"model_year" => 2020
},
"options" => {
"allow_mileage_estimation" => true
}
})
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts JSON.pretty_generate(JSON.parse(response.body))
import json
import os
import requests
response = requests.post(
"https://data-services.autouncle.com/v1/valuations",
headers={
"Authorization": f"Bearer {os.environ['AUTOUNCLE_API_KEY']}",
"Idempotency-Key": "valuation-mileage-baseline-001",
},
json={
"country": "DK",
"vehicle": {
"make": "Volkswagen",
"model": "Golf",
"model_year": 2020
},
"options": {
"allow_mileage_estimation": True
}
},
timeout=30,
)
print(json.dumps(response.json(), indent=2))
const response = await fetch("https://data-services.autouncle.com/v1/valuations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOUNCLE_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "valuation-mileage-baseline-001",
},
body: JSON.stringify({
country: "DK",
vehicle: {
make: "Volkswagen",
model: "Golf",
model_year: 2020
},
options: {
allow_mileage_estimation: true
}
}),
});
console.log(JSON.stringify(await response.json(), null, 2));
This uses a fixed baseline of 96,000 km, converted to the market's unit. It is an assumption, not an estimate of this car's mileage. The result:
- reports the baseline in
vehicle.vehicle.odometer.km, withsource: "imputed",basis: "assumed"andstatus: "assumed"at/vehicle/odometerinidentification.fields; - carries the
km_imputedwarning and animprove_precisionhint asking for the actual mileage; - is capped at
lowprecision.
{
"warnings": [
{ "code": "km_imputed", "message": "Mileage uses a fixed 96,000 km baseline, converted to the market's mileage unit. This is an assumption, not an estimate based on this vehicle. Supply the actual mileage before relying on this valuation." }
],
"improve_precision": [
{ "field": "mileage", "reason": "imputed", "expected_impact": "high", "message": "Provide the actual mileage." }
]
}
Show these to users. Never present a baseline result as a valuation of the actual car. The default is false, and the console playground's form has the same opt-in as an unchecked box.
Missing year
The year behaves differently from mileage. In the default resolution, a missing registration year is assumed: the result carries a year_imputed warning and an improve_precision hint with reason imputed for model_year. Send model_year or first_registration whenever you can.
Strict resolution
options.resolution: "strict" is for workflows where an assumption is never acceptable, such as lending or insurance decisions:
{
"options": { "resolution": "strict" }
}
Strict mode:
- refuses a missing registration year or mileage, even when
allow_mileage_estimationistrue; - never calls AI interpretation (like
no_ai); - otherwise values normally.
Combine it with catalogue IDs and min_precision to get either a well-founded result or a free refusal:
curl https://data-services.autouncle.com/v1/valuations \
-H "Authorization: Bearer $AUTOUNCLE_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: valuation-strict-001' \
-d @- <<'JSON'
{
"country": "DK",
"vehicle": {
"catalogue": {
"drivetrain_id": "dv-947a3199394a5a4b41cb2869"
},
"model_year": 2021,
"mileage": {
"value": 45000,
"unit": "km"
}
},
"options": {
"resolution": "strict",
"min_precision": "medium"
}
}
JSON
require "json"
require "net/http"
uri = URI("https://data-services.autouncle.com/v1/valuations")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("AUTOUNCLE_API_KEY")}"
request["Content-Type"] = "application/json"
request["Idempotency-Key"] = "valuation-strict-001"
request.body = JSON.generate({
"country" => "DK",
"vehicle" => {
"catalogue" => {
"drivetrain_id" => "dv-947a3199394a5a4b41cb2869"
},
"model_year" => 2021,
"mileage" => {
"value" => 45000,
"unit" => "km"
}
},
"options" => {
"resolution" => "strict",
"min_precision" => "medium"
}
})
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts JSON.pretty_generate(JSON.parse(response.body))
import json
import os
import requests
response = requests.post(
"https://data-services.autouncle.com/v1/valuations",
headers={
"Authorization": f"Bearer {os.environ['AUTOUNCLE_API_KEY']}",
"Idempotency-Key": "valuation-strict-001",
},
json={
"country": "DK",
"vehicle": {
"catalogue": {
"drivetrain_id": "dv-947a3199394a5a4b41cb2869"
},
"model_year": 2021,
"mileage": {
"value": 45000,
"unit": "km"
}
},
"options": {
"resolution": "strict",
"min_precision": "medium"
}
},
timeout=30,
)
print(json.dumps(response.json(), indent=2))
const response = await fetch("https://data-services.autouncle.com/v1/valuations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AUTOUNCLE_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "valuation-strict-001",
},
body: JSON.stringify({
country: "DK",
vehicle: {
catalogue: {
drivetrain_id: "dv-947a3199394a5a4b41cb2869"
},
model_year: 2021,
mileage: {
value: 45000,
unit: "km"
}
},
options: {
resolution: "strict",
min_precision: "medium"
}
}),
});
console.log(JSON.stringify(await response.json(), null, 2));
With a test key this particular example is refused with precision_below_minimum, because test results are always indicative. With a live key it returns a result of at least medium precision, or a refusal.
Refused or failed?
refused |
failed |
|
|---|---|---|
| Cause | The input | A service (timeout, outage, invalid response) |
| HTTP | 4xx | 5xx |
retryable |
false |
Usually true |
| What to do | Fix the input, new key | Back off, then retry with a new key |
| Charged | No | No |
See errors & retries for failures and retrieve, replay & reconcile for handling unknown outcomes.
Next steps
- Improve precision with hints.
- Errors & retries.
- Test with test keys to exercise refusals on purpose.