The problem: an image that has to work in March
Every account on Car Image API gets a short note from Jonathan about an hour after its first render. The obvious thing to put in that email is the render itself: the exact 2024 Porsche 911 or 2006 Dodge Charger the person just asked for, as proof the integration worked and as a small brag.
Email is the worst possible surface for a signed URL. A page is re-rendered on every visit; an email is rendered once and opened whenever the reader gets to it, a day, a month or a year later, often through Gmail's image proxy. The signed delivery URLs the API already offered had a maximum TTL of seven days, which is the right ceiling for a browser but guarantees a broken image in an inbox by week two. Attaching or inlining the PNG bloats every message.
What we wanted was a URL that behaves like a hosted image, keeps working for as long as anyone might open the email, and still charges the account fairly for what it actually serves.
The solution: auto-renewing signed URLs
We added one flag to POST /api/v1/image-urls: renew: true. A renewable URL is minted exactly like any other, counts its image as a vehicle image of the month, and is valid for ttl_seconds. The difference is what happens after the TTL. Instead of a 403, the first load in each new window of ttl_seconds renews the URL and serves the image; a renewal counts the image once in the month it lands in, and nothing more if it was already counted that month. Every further load in that window is publicly cacheable, the same as the first window. That continues until renews_until, up to a year out.
Three properties fell out of that design, and each one mattered for email:
- Nothing to re-send or re-mint. The URL in the email is the URL forever. No cron job rewriting stored links, no second email with a fresh image.
- Count opens, not time. A window that nobody opens counts nothing. An email read by one person on one day is the one vehicle image it was minted for; one opened every week for a year counts that image once in each month it is opened, twelve times at most.
- Idempotent counting. Each window is recorded once, keyed
renew:<url id>:<window>, unique per URL and window. Fifty opens in a week, or two mail proxies racing on the first open, count once.
The lifecycle emails mint these URLs through the same code path the public endpoint uses, as platform tokens: bound to no API key and never billed, because they are ours, but served by the identical GET /api/v1/delivery/{token} route, with the identical seven-day windows and the identical caching. The image in the first-render email is the product demonstrating itself.
curl -X POST https://carimage.dev/api/v1/image-urls \
-H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"images":[{"make":"porsche","model":"911","year":2024,"view":"front-3-4","color":"red","width":480}],"ttl_seconds":604800,"renew":true}'{
"data": [
{
"id": "3f0c2a7e-1c1e-4c2a-9c39-4f1f0a4b2d10",
"url": "https://carimage.dev/api/v1/delivery/eyJhbGciOi…",
"expires_at": "2026-09-21T09:00:00.000Z",
"max_uses": 0,
"renews_until": "2027-09-14T09:00:00.000Z",
"vehicle": {
"make": "porsche",
"model": "911",
"year": 2024,
"view": "front-3-4",
"color": "red",
"width": 480,
"format": "png"
}
}
],
"billing": {
"charged_on": "creation",
"credits_charged": 1,
"credits_remaining": 4869,
"credits_per_url": 1,
"renewal": {
"window_seconds": 604800,
"credits_per_window": 1,
"until": "2027-09-14T09:00:00.000Z"
}
},
"request_id": "req_01j9x…"
}ttl_seconds: 604800 (seven days, the maximum) means at most one renewal a week per image, and the image counts at most once a month however often it renews. A one-hour TTL with renew: true works too, but a busy inbox would renew every hour it is opened.The email itself
The template is React Email with inline styles, because mail clients strip stylesheets. The render sits in a bordered table cell on a soft background, requested at 480 pixels and shown at 240 so it stays crisp on retina screens, with a pill above it and two caption lines under it. Transparent PNG is the whole point of the product, so we keep it; on an old Outlook that paints alpha black, a format: "jpg" render on a solid cell background is the fallback.
<!-- inline styles only: mail clients strip stylesheets -->
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center" style="padding:24px;background:#fafaf9;border:1px solid #e7e5e4;border-radius:16px;">
<img
src="https://carimage.dev/api/v1/delivery/eyJhbGciOi…"
alt="2024 Porsche 911, red, front three-quarter view"
width="240" height="240"
style="display:block;margin:0 auto;max-width:100%;height:auto;"
/>
<p style="margin:14px 0 2px;font:700 18px/26px -apple-system,Segoe UI,Roboto,sans-serif;color:#1c1917;">2024 Porsche 911</p>
<p style="margin:0;font:13px/20px ui-monospace,Menlo,monospace;color:#78716c;">Front three-quarter · Red</p>
</td>
</tr>
</table>Everything else in the message is text: what the URL is, why it keeps working, the exact POST to mint one for their own vehicle, and a link to this page. The email carries an unsubscribe link and the List-Unsubscribe headers, and every product link is tagged so we can see, in the acquisition report, whether the story you are reading brought anyone back.
Do it yourself in three steps
- Mint server-side, when you build the email. One call per vehicle, or up to fifty per call for a digest. Store
urlandrenews_untilwith the message. - Put the URL in an
<img>with width, height and alt text. Alt text names the vehicle ("2024 Porsche 911, red, front three-quarter view"), not "car". - Keep the account funded. A renewal counts the image once in the month it lands in; if the plan's vehicle images are used up it answers 402 until the allowance resets on the 1st, so size the plan to the images you serve.
import { CarImageClient } from "@meterapp/car-image-sdk";
const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });
// One call per email: the vehicle the person rendered, at 2x the display width.
const { data } = await client.createImageUrls(
[{ make: "porsche", model: "911", year: 2024, view: "front-3-4", color: "red", width: 480 }],
{ ttlSeconds: 604_800, renew: true, renewDays: 365 }
);
const src = data[0].url; // drop into <img src>, store data[0].renews_until next to itThe full parameter reference, caching headers and error codes are on the signed URL docs. The agent skill that ships with the plugin knows the same rules, so an assistant asked to "put this car in the newsletter" mints a renewable URL instead of pasting a key into a template.
Frequently asked questions
- Why not attach the image or inline it as base64?
- Attachments and base64 bloat every message. A signed URL is a few hundred bytes and loads through the mail client's image proxy like any hosted image.
- What does an auto-renewing URL cost?
- One vehicle image of the month when it is created, then the same image counted once in each later month in which a renewal window is actually opened, until renews_until; a month in which nobody opens the email counts nothing. Pro, at $49 a month, includes 2,500 vehicle images a month.
- What happens when the month's vehicle images are used up?
- The load answers 402 and the image shows as broken until the month's allowance resets or the plan is raised; the URL itself stays valid and resumes on its next load. An image already counted this month keeps serving.
- Does this work in Gmail, Outlook and Apple Mail?
- Yes. The delivery endpoint is a plain public GET with Cache-Control, an ETag and CORS headers, which is exactly what image proxies expect. PNG transparency renders in every current client; for very old Outlook versions request format jpg on a solid background.
- Can a URL be revoked?
- Revoke the API key that minted it and every URL from that key answers 403 on its next load, renewals included. Shared caches keep serving for at most an hour after that.