← Car Image APICONTENT HISTORY

Update to Car Image API

Snapshot Oct 29, 2026 · 19:09 UTC · version 1.15.5

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save Car Image API and check future changes in Following.

Up to 10 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Share a vehicle image in chat (clickable links and browser viewing in ChatGPT/Codex, inline images in Claude) or put it into a web page, React component, email, Markdown file, PDF or slide deck using signed delivery URLs, so the browser loads the image without ever holding an API key. Use whenever the output is HTML, JSX, Markdown, CSS, an email template, a document or a spreadsheet rather than bytes on disk; covers looking each vehicle up once and minting by its id, TTL and use caps, batching up to 50, inventory grids from a list of VINs, caching, alt text and expiry. Do not use for downloading bytes in a script (car-image), or for finding which vehicle to render (vehicle-catalog).",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 509
    }
  ],
  "name": "car-image-urls",
  "skill_md_contents": "---\nname: car-image-urls\ndescription: Share a vehicle image in chat (clickable links and browser viewing in ChatGPT/Codex, inline images in Claude) or put it into a web page, React component, email, Markdown file, PDF or slide deck using signed delivery URLs, so the browser loads the image without ever holding an API key. Use whenever the output is HTML, JSX, Markdown, CSS, an email template, a document or a spreadsheet rather than bytes on disk; covers looking each vehicle up once and minting by its id, TTL and use caps, batching up to 50, inventory grids from a list of VINs, caching, alt text and expiry. Do not use for downloading bytes in a script (car-image), or for finding which vehicle to render (vehicle-catalog).\n---\n\n# Signed delivery URLs\n\nA signed URL is a key-free link to one rendered vehicle image. You mint it server-side; the browser, email client or PDF reader loads it directly. **The API key never goes where a browser can read it: a page gets a signed URL, or a copy of the file you serve yourself.**\n\n## Chat replies\n\nFor a chat image, resolve the vehicle first, then mint once with `ttl_seconds: 604800`, `max_uses: 0` and renewal off unless requested. The `car-image` skill covers random-car selection and the chat workflow.\n\nIn ChatGPT and Codex, show each exact returned `data[].url` as a normal clickable Markdown link, for example `[View the vehicle image](https://example.com/image.png)` with the returned URL substituted. Do not use Markdown image syntax or paste the tool's image `markdown` into these chats, even if generic tool text recommends it. Open that same URL with an available browser-opening tool, preferably in the in-app browser (for example, `open_in_codex` with a browser target). Retain the clickable link in the final reply whether opening succeeds or not. If no browser tool is available, just provide the link. Do not claim the browser opened unless the tool confirms it.\n\nIn Claude, keep rendering inline: paste each returned `data[].markdown` outside code fences. If only `url` is returned, use `![year make model, view, color](https://example.com/image.png)` with the exact returned URL. Do not automatically open a browser. Other hosts may use inline images when supported, otherwise a clickable link.\n\nInclude a short vehicle caption and expiry. Reuse unexpired URLs; do not download the image or make another paid image call to work around chat display. These host-specific rules apply to chat replies; website and document embeds still use image markup.\n\n## Why not just call the image endpoint from the page\n\n`GET /api/v1/images/car` needs `Authorization: Bearer cimg_…`. Anything in a browser — an `<img src>`, a `fetch` in client-side JavaScript, a CSS `background-image` — is readable by the user and by anyone they share the page with. Putting the key there leaks it permanently.\n\nSigned URLs solve this: the signature authorizes exactly one image for a limited time, and carries no key.\n\n## The call\n\nLook each vehicle up first (`resolve_vehicle`, `search_vehicles` or `decode_vin`; all free, the `vehicle-catalog` skill) and mint by its id. A name from memory can miss a car the catalog carries, and in a batch one wrong name fails the whole call.\n\nWith MCP connected, use `create_car_image_urls`. Over REST it is `POST /api/v1/image-urls`:\n\n```json\n{\n  \"images\": [\n    { \"vehicle\": \"veh_78qtwrgh37bkr\", \"view\": \"side\", \"color\": \"red\", \"width\": 768, \"height\": 432, \"trim\": true, \"format\": \"auto\" }\n  ],\n  \"ttl_seconds\": 86400,\n  \"max_uses\": 0\n}\n```\n\nAn entry may spell the vehicle out instead, in the catalog's spelling:\n\n```json\n{\n  \"images\": [\n    { \"make\": \"Porsche\", \"model\": \"911\", \"year\": 2024, \"view\": \"side\", \"color\": \"red\", \"width\": 768, \"height\": 432, \"trim\": true, \"format\": \"auto\" }\n  ],\n  \"ttl_seconds\": 86400,\n  \"max_uses\": 0\n}\n```\n\nThe MCP tool also returns ready-to-display `markdown` for each image. Each REST entry returns `id`, `url`, `expires_at`, `max_uses`, `renews_until` and the normalized vehicle (which opens with `vehicle_id` and echoes `fit`, `background`, `trim` and `padding`). `\"vehicle\"` and make, model and year together are a `400`, and `\"color\": \"#1a2b3c\"` is any paint at the same price.\n\n- **`images`** — 1 to 50 per call. Body limit is 64 KiB; split larger jobs into batches of 50. Each entry takes the same sizing options as `get_car_image`: `size` or `width`/`height` (delivered up to 1024, a larger size clamped; both together return exactly that box), `fit` (`contain` default, `cover`, `inside`), `background` (`transparent` default, a CSS color name such as `white`, or hex), `trim` with `padding` (0–50 %), and `format` (`png`, `webp`, `jpg`, `auto`). The `car-image` skill has the full table.\n- **`ttl_seconds`** — 60 to 604800 (7 days). Default 3600.\n- **`max_uses`** — `0` means unlimited loads until expiry. A positive number caps redemptions and returns `410` afterwards.\n- **`renew`** — `true` makes the URL auto-renew: the first load in each further window of `ttl_seconds` bills one more credit and keeps it alive, until `renew_days` (1–365, default 365) from creation. Requires `max_uses: 0`. See \"Surfaces that outlive a TTL\" below.\n- **`Idempotency-Key` header** — send one (1–255 characters of letters, digits, `.` `_` `:` `-`) whenever the call might be repeated: a job queue, a build step, a retried request. The same key with the same body within 24 hours replays the first response (`Idempotent-Replayed: true`) instead of minting and billing a second batch; the same key with a different body is a `422`; a retry that overtakes the original still in flight is a `409` with `Retry-After`. The SDK sends a generated key on every `createImageUrls` call (pass `idempotencyKey` to choose it), the CLI takes `--idempotency-key`, and the MCP tool takes `idempotency_key` as an argument (a replay comes back with `idempotent_replayed: true`).\n\n## What it costs\n\n**1 credit per URL, charged when the URL is created.** Loading it is free, however many times, until it expires. Ten URLs cost 10 credits (1¢) whether they are loaded once or a million times.\n\nSo: mint once, cache the URL, reuse it. Do not re-mint the same image on every page render — that bills a credit every time.\n\n## Choosing a TTL\n\nThe TTL is a cache lifetime, not a security boundary — anyone with the URL can load it until it expires. Match it to how the page is served.\n\n| Surface | TTL | Why |\n| --- | --- | --- |\n| Static site, build-time generation | 7 days (`604800`), re-mint on each build | Longest window; the build owns the refresh. |\n| Server-rendered page, cached in your DB | 1-7 days | Store `url` and `expires_at`; re-mint when `expires_at` is near. |\n| Per-request SSR with no storage | 1 hour (default) | The page outlives the URL only if the user leaves the tab open. |\n| Email | 7 days, `max_uses: 0`, `renew: true` | Mail is opened days, months or a year later. A renewable URL keeps working at one credit per opened week; a plain 7-day URL is a broken image by week two. |\n| CMS page or PDF nobody re-publishes | 7 days, `renew: true` | Same reasoning as email: the surface is rendered once and read for a long time. |\n| One-off share link | Short TTL plus `max_uses` | The only case where a use cap helps. |\n\n**Never mint a URL inside a React render, a `useEffect`, or a hot request path without caching.** That is a credit per call.\n\n## Surfaces that outlive a TTL\n\nAn email, a PDF or a page in a CMS nobody re-publishes is rendered once and opened whenever the reader gets to it. For those, mint with `renew: true`:\n\n```json\n{ \"images\": [{ \"make\": \"Porsche\", \"model\": \"911\", \"year\": 2024, \"view\": \"front-3-4\", \"color\": \"red\", \"width\": 480 }], \"ttl_seconds\": 604800, \"renew\": true }\n```\n\n- The URL is created and charged like any other. When it is loaded after `expires_at`, the first load in each new window of `ttl_seconds` bills **one more credit** and renews it; every further load in that window is free and cacheable, exactly like the first window. Windows nobody opens cost nothing.\n- With the 7-day maximum TTL the ceiling is one credit per image per opened week, about five cents for an image opened every week for a year.\n- Billing is once per window whatever the open count: the ledger reference `renew:<id>:<window>` is unique, so two mail proxies racing on the first open pay once.\n- An account that cannot pay a renewal gets `402` on that load and the image shows as broken until credits land; the URL itself stays valid and resumes on its next load. Suggest auto-reload for production accounts.\n- Revoking the key still ends the URL (`403`), renewals included.\n- `renew` cannot be combined with a use cap (`max_uses` must be 0).\n\nStore `renews_until` next to the URL. The story at https://carimage.dev/customers/car-images-in-email?ref=plugin shows the exact email HTML.\n\n## Patterns\n\n### Next.js — server component\n\n```tsx\nimport { CarImageClient } from \"@meterapp/car-image-sdk\";\n\nconst client = new CarImageClient(); // server only: the key stays in the deployment's secrets (car-image-sdk)\n\nexport default async function Hero() {\n  const { data } = await client.createImageUrls(\n    [{ make: \"Porsche\", model: \"911\", year: 2024, view: \"side\", color: \"red\", width: 768, height: 432, trim: true, format: \"auto\" }],\n    { ttlSeconds: 86_400, idempotencyKey: \"hero-porsche-911-2024-side-red\" }\n  );\n  return <img src={data[0].url} alt=\"2024 Porsche 911, side view, red\" width={768} height={432} />;\n}\n```\n\n`width` and `height` together return exactly 768×432, so the `<img>` attributes match the bytes; `trim` makes the car fill that wide box instead of floating in a square. Wrap it in your data cache (`unstable_cache`, ISR, or your own table) keyed by the vehicle parameters so a page view does not mint a new URL — the idempotency key stops a retried build from paying twice, not a hot path from minting on every request.\n\n### A batch for a comparison grid\n\n```ts\nconst vehicles = [\n  { make: \"Toyota\", model: \"RAV4\", year: 2023 },\n  { make: \"Honda\", model: \"CR-V\", year: 2023 },\n  { make: \"Mazda\", model: \"CX-5\", year: 2023 },\n];\nconst { data } = await client.createImageUrls(\n  vehicles.map((v) => ({ ...v, view: \"front-3-4\", color: \"silver\", width: 512 })),\n  { ttlSeconds: 604_800 }\n);\n```\n\nOne call, three credits. Results come back in request order.\n\n### An inventory grid from a list of VINs\n\nA dealer page, a marketplace import, a fleet dashboard: the input is a spreadsheet of VINs or of year, make and model, and most rows are the same few vehicles.\n\n1. **Look up every row for free.** `decode_vin` for a VIN (`vehicle.id`), `resolve_vehicle` for a name (`params.vehicle_id`). Set aside the rows that do not resolve and report them; do not guess.\n2. **Deduplicate by vehicle id.** Forty Camrys of one model year are one image. The cost is distinct vehicles × views × paints, not rows.\n3. **Say the count and the cost, and get a yes.** \"212 rows, 37 distinct vehicles, one view each: 37 credits.\"\n4. **Mint in batches of 50** with an `idempotency_key` per batch, so a retried import does not pay twice.\n5. **Store `url`, `expires_at` and the `vehicle_id` next to each row**, and re-mint by id before expiry. A page nobody rebuilds wants `renew: true`.\n\nShow the decoded trim, body and engine as text beside the image; the render represents the model, not that car on the lot.\n\n### One fixed image: a hero, a card, a slide\n\nWhen the image never changes, a signed URL that expires is more machinery than the job needs. Download the file once and serve it from the project's own assets:\n\n```bash\nnpx @meterapp/car-image get --vehicle veh_78qtwrgh37bkr --view front-3-4 --color red --width 1024 --height 576 --trim --padding 6 --format webp --out public/hero-911.webp\n```\n\nOne credit, no expiry to track, no request at page load. Keeping copies on your own servers or CDN to serve your own product is part of a paid plan's license, for as long as the plan is active; the Free plan is for evaluation and personal projects, and a standalone library or bulk export needs a written agreement. `search_help` has the terms when the user asks (the `car-image-support` skill). Use signed URLs when the set of vehicles is open-ended: a catalog, a search result, a listing feed.\n\n### Markdown and email\n\n```md\n![2022 BMW M3, front three-quarter view, blue](https://carimage.dev/api/v1/delivery/eyJ2IjoxLCJ…)\n```\n\nTransparent PNG sits on any background, which is what you want in a document. For email, ask for a solid `background` (`\"white\"`, or the hex of the surrounding table cell) — it flattens PNG and WebP too, and `jpg` defaults to white — because some older mail clients render PNG transparency as black.\n\n## Getting the image right\n\n- **Transparent by default.** `format: \"png\"` has a real alpha channel, so the render drops onto any background without a box around it. Choose `webp` for smaller files with transparency, `jpg` only when the surface cannot do alpha, and `auto` for an `<img>` any browser will load: the URL negotiates WebP or PNG from each client's `Accept` header on every load.\n- **Ask for the box you will display**, up to 1024 on each side. With both `width` and `height` the image comes back exactly that size (`fit: \"contain\"`, the default, keeps the whole car and pads the rest; `\"cover\"` fills the box and crops). For a non-square slot add `trim: true` so the car fills it instead of floating in the square source frame, with `padding` (0–50 %) for breathing room. A 512 px render in a 256 px slot wastes bytes; a 256 px render in a 512 px slot looks soft, and nothing is upscaled past 1024. For retina, request 2× the CSS size.\n- **Set `width` and `height` on the `<img>`** so the page does not reflow while it loads — the values you asked for, since that is exactly what comes back.\n- **Alt text describes the vehicle**: \"2024 Porsche 911, side view, red\". Not \"car\", not \"image\".\n- **Cache publicly.** Delivery URLs are safe to put behind a CDN for their TTL; that is the point of them.\n\n## Expiry is a real state\n\n`expires_at` comes back with every URL. Whatever stores the URL must store that too.\n\n- Re-mint before expiry, not after a user reports a broken image.\n- A `403` on a delivery URL means expired, invalid, the key that created it was revoked, or `code: \"origin_not_allowed\"`: the page loading it is on a site the issuing key's allowed origins (set on the key in the dashboard) do not list; loads without a `Referer` or `Origin`, such as email clients, pass. A `410` means a use-capped URL hit its cap. Both are fixed by minting a fresh URL, not by retrying the old one. A `402` means a renewable URL entered a new window the account could not pay for; add credits and the same URL resumes.\n- In chat, follow the host-specific display rules above and state expiry: ChatGPT/Codex gets a clickable link and browser opening when available; Claude keeps inline Markdown. Reuse the same URL without another paid call.\n\n## Do not\n\n- Mint a URL for a vehicle you have not looked up. One `404` fails the whole batch before anything is charged; it carries `suggestions` (and, over REST, the `index` of the entry), so fix that entry and send the batch again.\n- Put an API key in client-side code, an `<img src>`, or a committed config file. Mint a signed URL instead.\n- Mint a URL per request or per render without caching — each one is a credit.\n- Treat a signed URL as private. It is unguessable, not access-controlled.\n- Present a render as a photograph of one specific listed vehicle. It represents the model.\n"
}

SHA-256 of public snapshot: 1c9fac69caa35b01d8195e59c28ce42fde1e2155fd325726a92e65e829882958