问题:一张到三月份仍须可用的图片
Car Image API 上的每个账户在首次渲染约一小时后,都会收到 Jonathan 发来的一封简短邮件。这封邮件里最顺理成章的内容就是渲染图本身:正是对方刚刚请求的那辆 2024 Porsche 911 或 2006 Dodge Charger,既证明集成已成功运行,也算小小地炫耀一下。
电子邮件是签名 URL 最糟糕的使用场景。网页每次访问都会重新渲染;而邮件只渲染一次,读者可能在一天、一个月甚至一年之后才打开,且常常经由 Gmail 的图片代理。API 此前提供的签名分发 URL 最长 TTL 为七天,这对浏览器而言是合理的上限,但放在收件箱里,到第二周图片必然失效。而将 PNG 作为附件或内嵌则会让每封邮件都变得臃肿。
我们想要的是这样一个 URL:表现得像托管图片一样,只要有人可能打开这封邮件就一直有效,同时仍按实际提供的内容向账户公平计费。
解决方案:自动续期的签名 URL
我们为 POST /api/v1/image-urls 新增了一个参数:renew: true。可续期 URL 的生成方式与其他 URL 完全相同,其图片计为当月的一张车辆图片,有效期为 ttl_seconds。区别在于 TTL 到期之后:不再返回 403,而是在每个新的 ttl_seconds 时间窗口内的首次加载时续期该 URL 并提供图片;续期会在其所在月份计一次该图片,如果该图片当月已计入,则不再额外计数。该窗口内的后续加载均可公开缓存,与第一个窗口相同。这一过程持续到 renews_until,最长可达一年。
这一设计自然带来了三个特性,每一个对邮件都至关重要:
- 无需重新发送或重新生成。邮件中的 URL 永久有效。无需定时任务改写已存储的链接,也无需再发一封带新图片的邮件。
- 按打开次数计,而非按时间计。无人打开的时间窗口不计入任何用量。一封仅由一人在某一天阅读的邮件,只计为当初为其生成的那一张车辆图片;一封一年内每周都被打开的邮件,在每个有打开的月份中各计一次该图片,最多计十二次。
- 幂等计数。每个时间窗口只记录一次,以
renew:<url id>:<window>为键,对每个 URL 和窗口唯一。一周内打开五十次,或两个邮件代理在首次打开时同时请求,都只计一次。
生命周期邮件通过与公共端点相同的代码路径生成这些 URL,作为平台令牌:它们不绑定任何 API 密钥,也从不计费,因为它们属于我们自己,但由完全相同的 GET /api/v1/delivery/{token} 路由提供服务,具有完全相同的七天有效期和完全相同的缓存机制。首次渲染邮件中的图片,正是产品在展示自己。
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(七天,即最大值)意味着每张图片每周最多续期一次,且无论续期多频繁,每张图片每月最多计一次。一小时的 TTL 配合 renew: true 同样可行,但活跃的收件箱在每个被打开的小时都会触发续期。邮件本身
模板采用 React Email 并使用内联样式,因为邮件客户端会剥离样式表。渲染图放在浅色背景上带边框的表格单元格中,以 480 像素请求、以 240 像素显示,以便在 Retina 屏幕上保持清晰;图片上方有一个胶囊标签,下方有两行说明文字。透明 PNG 正是本产品的核心,所以我们保留了它;对于会将透明通道渲染为黑色的旧版 Outlook,则以纯色单元格背景上的 format: "jpg" 渲染图作为后备方案。
<!-- 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>邮件中的其他内容都是文字:这个 URL 是什么、为什么它会一直有效、为自己的车辆生成 URL 所需的确切 POST 请求,以及指向本页的链接。邮件包含退订链接和 List-Unsubscribe 标头,每个产品链接都带有追踪标记,以便我们在获客报告中看到您正在阅读的这篇案例是否带回了用户。
三步自行实现
- 在构建邮件时于服务端生成。每辆车调用一次;若为摘要邮件,每次调用最多可生成五十个。将
url和renews_until与邮件一同存储。 - 将 URL 放入带有宽度、高度和替代文本的
<img>中。替代文本应写明具体车辆(“2024 Porsche 911,红色,前侧四分之三视角”),而不是“汽车”。 - 确保账户额度充足。续期会在其发生的月份将图片计入一次;如果套餐的车辆图片已用完,则会返回 402,直到每月 1 日配额重置,因此请根据您提供的图片数量选择合适的套餐。
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 it完整的参数参考、缓存标头和错误代码请参阅签名 URL 文档。插件附带的智能体技能也遵循相同规则,因此当助手被要求“把这辆车放进新闻简报”时,它会生成可续期的 URL,而不是把密钥粘贴到模板中。
常见问题
- 为什么不将图片作为附件,或以 base64 形式内嵌?
- 附件和 base64 会让每封邮件变得臃肿。签名 URL 只有几百字节,并会像任何托管图片一样通过邮件客户端的图片代理加载。
- 自动续期的 URL 费用是多少?
- 创建时计为当月的一张车辆图片,之后在每个实际打开续期窗口的月份将同一图片计一次,直至 renews_until;无人打开邮件的月份不计数。Pro 每月 US$49,每月包含 2,500 张车辆图片。
- 本月车辆图片用完后会怎样?
- 加载会返回 402,图片将显示为损坏,直到当月额度重置或套餐升级;URL 本身仍然有效,并会在下次加载时恢复。本月已计数的图片会继续正常提供。
- 这在 Gmail、Outlook 和 Apple Mail 中都能正常显示吗?
- 可以。交付端点是普通的公开 GET 请求,带有 Cache-Control、ETag 和 CORS 标头,这正是图片代理所需要的。所有当前的邮件客户端都能正确显示 PNG 透明背景;对于非常旧的 Outlook 版本,请以纯色背景请求 jpg 格式。
- URL 可以撤销吗?
- 吊销生成该 URL 的 API 密钥后,该密钥生成的所有 URL 在下次加载时都会返回 403,续期的 URL 也不例外。此后共享缓存最多还会继续提供一小时。