Skip to main content

Website themes

Dealers on a paid plan can ask Vehiso's AI theme builder for a new look for their website, from their own tools, exactly as they can from the DMS. You describe the theme in a brief, the builder makes it in the background, and you publish the result when you are happy with it. A build never goes live by itself.

ScopeAllows
website_themes:readRead theme builds and theme versions
website_themes:writeRequest builds and publish versions

Both need the key's owner to hold the website.theme permission in the DMS.

The flow​

  1. Request a build with POST /v1/theme-generations. It answers 202 with a generation whose status is queued.
  2. Follow it by polling GET /v1/theme-generations/{id}, or with webhooks. The status moves from queued to running, and ends as succeeded or failed.
  3. Preview it. A succeeded generation carries the theme version it built and, on the GET, a preview_url.
  4. Publish it with POST /v1/theme-versions/{version}/publish.

1. Request a build​

Send a brief describing the theme. You can add a reference_url for a website to take the look from. Set reference_content_consented to true only if the dealer may use that site's content, not just its style.

curl https://api.vehiso.com/v1/theme-generations \
-H "Authorization: Bearer $VEHISO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6a1d3e8f-2b4c-4d5e-9f7a-0b1c2d3e4f5a" \
-d '{"brief": "Dark, understated premium look for a used prestige car dealer. Large photography, minimal text, gold accents."}'
{
"data": {
"id": "0e4b2f6a-9c3d-4a1e-8b7f-5d2c1a0e9f84",
"kind": "generate",
"status": "queued",
"brief": "Dark, understated premium look for a used prestige car dealer. Large photography, minimal text, gold accents.",
"instruction": null,
"reference_url": null,
"version": null,
"findings": [],
"failure_reason": null,
"created_at": "2026-09-27T14:02:11Z",
"finished_at": null
}
}

To change the latest theme rather than start again, send "kind": "revise" with an instruction instead of a brief:

{"kind": "revise", "instruction": "Make the header smaller and use the dealer's blue for buttons."}

Each dealer has a monthly allowance of builds. Send an Idempotency-Key so a retried request does not spend a second one.

2. Follow the build​

const headers = {Authorization: `Bearer ${process.env.VEHISO_API_KEY}`};
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export async function waitForTheme(generationId) {
for (;;) {
const res = await fetch(`https://api.vehiso.com/v1/theme-generations/${generationId}`, {headers});
const {data} = await res.json();
if (data.status === 'succeeded' || data.status === 'failed') return data;
await sleep(10000);
}
}

A build takes a while, so poll every few seconds at most. Instead of polling, you can subscribe a webhook endpoint to theme_generation.started, theme_generation.succeeded and theme_generation.failed. The events carry the generation in data.object, and they fire for every build, whether it was requested through the API, in the DMS or by the assistant.

A failed generation says why in failure_reason: quota, entitlement or generation_failed.

3. Preview it​

Once a generation has succeeded, version is the theme version it built, and findings lists any lint findings on it (each with a severity of error or warning, a path, a line and a message).

GET /v1/theme-generations/{id} also returns a preview_url: a signed link that shows the new version on the dealer's site before it goes live. It expires after 30 minutes, so fetch the generation again for a fresh link rather than storing it.

GET /v1/theme-versions lists every version, generated or uploaded, with a count of its findings and which one is live. Unlike other lists it is paged by number, with page and per_page (up to 100), and meta.last_page and meta.total.

4. Publish it​

curl -X POST https://api.vehiso.com/v1/theme-versions/12/publish \
-H "Authorization: Bearer $VEHISO_API_KEY"
{"data": {"version": 12, "is_live": true}}

Publishing refuses a version that is not safe to put live with 422 theme_publish_blocked. errors.routes lists pages the theme does not render, and errors.findings its error-severity lint findings, as path:line: message. Request a revision that fixes them, then publish the new version.

Errors​

StatuscodeMeaning
403theme_builder_not_enabledThe dealer has not turned on the theme builder. Any dealer on a paid plan can turn it on in the DMS under Administration > Feature previews.
403plan_not_entitledThe dealer needs a paid plan.
429theme_generation_quota_exceededThis month's builds are used up. This is not a rate limit: there is no Retry-After, and retrying soon will not help.
422theme_publish_blockedThe version is missing required pages or has error findings. See above.

The full request and response bodies are in the API reference.