Car Image API

Khách hàng · Car Image API

Cách nhúng ảnh xe vào email bằng signed URL không bao giờ bị hỏng

Car Image API đưa bản render đầu tiên của mỗi khách hàng vào hộp thư của họ với một URL có chữ ký tự động gia hạn: cơ chế, phép tính và mã để bạn tự làm.

Vấn đề: một hình ảnh vẫn phải dùng được vào tháng Ba

Mỗi tài khoản trên Car Image API đều nhận được một lời nhắn ngắn từ Jonathan khoảng một giờ sau lần render đầu tiên. Thứ hiển nhiên nhất để đưa vào email đó chính là ảnh render: đúng chiếc Porsche 911 2024 hay Dodge Charger 2006 mà người đó vừa yêu cầu, vừa là bằng chứng tích hợp đã hoạt động, vừa là một chút khoe khoang.

Email là nơi tệ nhất có thể để dùng URL có chữ ký. Một trang web được render lại mỗi lần truy cập; một email chỉ được render một lần và được mở bất cứ khi nào người đọc xem đến, một ngày, một tháng hay một năm sau, thường qua proxy hình ảnh của Gmail. Các URL phân phối có chữ ký mà API cung cấp trước đó có TTL tối đa bảy ngày, là giới hạn hợp lý cho trình duyệt nhưng chắc chắn khiến ảnh trong hộp thư bị lỗi từ tuần thứ hai. Đính kèm hoặc nhúng trực tiếp PNG thì làm mọi thư trở nên nặng nề.

Điều chúng tôi muốn là một URL hoạt động như một ảnh được lưu trữ, vẫn dùng được chừng nào còn có người có thể mở email, mà vẫn tính phí tài khoản công bằng theo những gì thực sự được phục vụ.

Giải pháp: URL có chữ ký tự động gia hạn

Chúng tôi đã thêm một cờ vào POST /api/v1/image-urls: renew: true. URL có thể gia hạn được tạo giống hệt mọi URL khác, tính hình ảnh của nó là một hình ảnh xe của tháng và có hiệu lực trong ttl_seconds. Điểm khác biệt là những gì xảy ra sau TTL. Thay vì trả về 403, lượt tải đầu tiên trong mỗi khung ttl_seconds mới sẽ gia hạn URL và phục vụ hình ảnh; mỗi lần gia hạn tính hình ảnh một lần trong tháng diễn ra, và không tính thêm nếu hình ảnh đã được tính trong tháng đó. Mọi lượt tải tiếp theo trong khung đó đều có thể được cache công khai, giống như khung đầu tiên. Việc này tiếp tục cho đến renews_until, tối đa một năm.

Thiết kế đó mang lại ba đặc tính, và mỗi đặc tính đều quan trọng đối với email:

  • Không cần gửi lại hay tạo lại. URL trong email là URL vĩnh viễn. Không có cron job viết lại các liên kết đã lưu, không có email thứ hai kèm ảnh mới.
  • Tính theo lượt mở, không theo thời gian. Một khung thời gian không ai mở thì không tính gì. Một email được một người đọc trong một ngày chỉ tính đúng một hình ảnh xe mà nó được tạo cho; một email được mở mỗi tuần trong một năm sẽ tính hình ảnh đó một lần trong mỗi tháng có lượt mở, tối đa mười hai lần.
  • Đếm lũy đẳng. Mỗi khoảng thời gian chỉ được ghi nhận một lần, với khóa renew:<url id>:<window>, duy nhất theo từng URL và khoảng thời gian. Năm mươi lượt mở trong một tuần, hay hai proxy thư cùng tranh nhau ở lượt mở đầu tiên, đều chỉ tính một lần.

Các email vòng đời tạo các URL này qua cùng luồng mã mà endpoint công khai sử dụng, dưới dạng token nền tảng: không gắn với API key nào và không bao giờ bị tính phí, vì chúng là của chúng tôi, nhưng được phục vụ qua đúng route GET /api/v1/delivery/{token}, với cùng thời hạn bảy ngày và cùng cơ chế bộ nhớ đệm. Hình ảnh trong email về lần render đầu tiên chính là sản phẩm đang tự minh chứng.

Tạo một URL có thể gia hạn cho ảnh render đầu tiên của khách hàng
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}'
201 Created
{
  "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…"
}

Bản thân email

Mẫu email được viết bằng React Email với style nội tuyến, vì các ứng dụng email loại bỏ stylesheet. Ảnh render nằm trong một ô bảng có viền trên nền dịu, được yêu cầu ở 480 pixel và hiển thị ở 240 để luôn sắc nét trên màn hình retina, với một nhãn bo tròn phía trên và hai dòng chú thích phía dưới. PNG nền trong suốt là cốt lõi của sản phẩm nên chúng tôi giữ nguyên; với Outlook đời cũ tô kênh alpha thành màu đen, phương án dự phòng là ảnh render format: "jpg" đặt trên ô có nền màu đặc.

Thẻ hiển thị, dưới dạng HTML thuần
<!-- 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>

Phần còn lại của thư là văn bản: URL đó là gì, vì sao nó tiếp tục hoạt động, lệnh POST chính xác để tạo một URL cho chính chiếc xe của họ, và một liên kết đến trang này. Email có liên kết hủy đăng ký và các header List-Unsubscribe, và mọi liên kết sản phẩm đều được gắn thẻ để chúng tôi có thể thấy trong báo cáo thu hút người dùng liệu câu chuyện bạn đang đọc có đưa ai quay lại hay không.

Tự làm trong ba bước

  1. Tạo phía server, khi bạn dựng email. Mỗi lần gọi cho một xe, hoặc tối đa năm mươi xe mỗi lần gọi cho email tổng hợp. Lưu url và renews_until cùng với thư.
  2. Đặt URL vào thẻ <img> kèm chiều rộng, chiều cao và văn bản alt. Văn bản alt nêu rõ tên xe ("2024 Porsche 911, màu đỏ, góc chéo trước ba phần tư"), không chỉ ghi "xe".
  3. Duy trì tài khoản đủ hạn mức. Mỗi lần gia hạn tính ảnh một lần trong tháng diễn ra gia hạn; nếu đã dùng hết ảnh xe của gói, yêu cầu sẽ trả về 402 cho đến khi hạn mức được đặt lại vào ngày 1, vì vậy hãy chọn gói phù hợp với số ảnh bạn phân phối.
Với TypeScript SDK
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

Tài liệu tham khảo đầy đủ về tham số, header bộ nhớ đệm và mã lỗi có trong tài liệu signed URL. Agent skill đi kèm plugin cũng nắm các quy tắc này, nên khi một trợ lý được yêu cầu "đưa chiếc xe này vào bản tin", nó sẽ tạo một URL có thể gia hạn thay vì dán khóa vào mẫu.

Câu hỏi thường gặp

Tại sao không đính kèm ảnh hoặc nhúng trực tiếp dưới dạng base64?
Tệp đính kèm và base64 làm phình to mọi email. Một URL có chữ ký chỉ nặng vài trăm byte và được tải qua proxy ảnh của ứng dụng email như mọi ảnh được lưu trữ trực tuyến khác.
URL tự động gia hạn có giá bao nhiêu?
Một hình ảnh xe của tháng khi được tạo, sau đó cùng hình ảnh đó được tính một lần trong mỗi tháng tiếp theo mà cửa sổ gia hạn thực sự được mở, cho đến renews_until; tháng nào không ai mở email thì không tính gì. Gói Pro, giá 49 US$ mỗi tháng, bao gồm 2.500 hình ảnh xe mỗi tháng.
Điều gì xảy ra khi đã dùng hết hình ảnh xe của tháng?
Lượt tải trả về 402 và hình ảnh hiển thị bị lỗi cho đến khi hạn mức của tháng được đặt lại hoặc gói được nâng cấp; bản thân URL vẫn hợp lệ và hoạt động lại ở lượt tải tiếp theo. Hình ảnh đã được tính trong tháng này vẫn tiếp tục hiển thị.
Cách này có hoạt động trong Gmail, Outlook và Apple Mail không?
Có. Endpoint phân phối là một yêu cầu GET công khai thông thường với Cache-Control, ETag và header CORS, đúng như những gì các image proxy mong đợi. Độ trong suốt của PNG hiển thị trên mọi ứng dụng email hiện nay; với các phiên bản Outlook rất cũ, hãy yêu cầu định dạng jpg trên nền đặc.
URL có thể bị thu hồi không?
Khi thu hồi khóa API đã tạo ra URL đó, mọi URL từ khóa này sẽ trả về 403 ở lần tải kế tiếp, kể cả các lần gia hạn. Bộ nhớ đệm dùng chung vẫn có thể tiếp tục phục vụ tối đa một giờ sau đó.