Problema: o imagine care trebuie să funcționeze și în martie
Fiecare cont Car Image API primește un scurt mesaj de la Jonathan la aproximativ o oră după prima sa randare. Cel mai evident lucru de pus în acel e-mail este chiar randarea: exact Porsche 911 din 2024 sau Dodge Charger din 2006 pe care persoana tocmai l-a cerut, ca dovadă că integrarea a funcționat și ca mică laudă.
E-mailul este cea mai nepotrivită suprafață posibilă pentru un URL semnat. O pagină este randată din nou la fiecare vizită; un e-mail este randat o singură dată și deschis oricând ajunge cititorul la el, peste o zi, o lună sau un an, adesea prin proxy-ul de imagini al Gmail. URL-urile semnate de livrare pe care API-ul le oferea deja aveau un TTL maxim de șapte zile, plafonul potrivit pentru un browser, dar care garantează o imagine nefuncțională în inbox până în a doua săptămână. Atașarea sau includerea inline a PNG-ului îngreunează fiecare mesaj.
Ce ne doream era un URL care să se comporte ca o imagine găzduită, să funcționeze cât timp cineva ar putea deschide e-mailul și totuși să taxeze contul corect pentru ceea ce servește efectiv.
Soluția: URL-uri semnate cu reînnoire automată
Am adăugat un singur parametru la POST /api/v1/image-urls: renew: true. Un URL reînnoibil se generează exact ca oricare altul, își numără imaginea ca imagine de vehicul a lunii și este valabil ttl_seconds. Diferența apare după expirarea TTL-ului. În loc de un 403, prima încărcare din fiecare nouă fereastră de ttl_seconds reînnoiește URL-ul și servește imaginea; o reînnoire numără imaginea o dată în luna în care are loc și nimic în plus dacă a fost deja numărată în acea lună. Orice altă încărcare din fereastra respectivă poate fi memorată public în cache, la fel ca în prima fereastră. Așa continuă până la renews_until, cu cel mult un an înainte.
Din acest design au rezultat trei proprietăți, iar fiecare a contat pentru e-mail:
- Nimic de retrimis sau de regenerat. URL-ul din e-mail rămâne același pentru totdeauna. Fără cron job care rescrie linkurile stocate, fără un al doilea e-mail cu o imagine nouă.
- Se numără deschiderile, nu timpul. O fereastră în care nimeni nu deschide e-mailul nu se numără deloc. Un e-mail citit de o singură persoană într-o singură zi contează ca o singură imagine de vehicul, cea pentru care a fost emis URL-ul; unul deschis în fiecare săptămână timp de un an numără acea imagine o dată în fiecare lună în care este deschis, de cel mult douăsprezece ori.
- Contorizare idempotentă. Fiecare fereastră este înregistrată o singură dată, cu cheia
renew:<url id>:<window>, unică pentru fiecare URL și fereastră. Cincizeci de deschideri într-o săptămână sau două proxy-uri de e-mail care concurează la prima deschidere se contorizează o singură dată.
E-mailurile de ciclu de viață generează aceste URL-uri prin aceeași cale de cod pe care o folosește endpointul public, ca tokenuri de platformă: nelegate de vreo cheie API și niciodată facturate, pentru că sunt ale noastre, dar servite de aceeași rută GET /api/v1/delivery/{token}, cu aceleași ferestre de șapte zile și același caching. Imaginea din e-mailul primei randări este produsul care se demonstrează singur.
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 (șapte zile, maximul) înseamnă cel mult o reînnoire pe săptămână per imagine, iar imaginea se numără cel mult o dată pe lună, oricât de des s-ar reînnoi. Un TTL de o oră cu renew: true funcționează și el, dar o căsuță de e-mail foarte activă l-ar reînnoi în fiecare oră în care este deschis.E-mailul propriu-zis
Șablonul este React Email cu stiluri inline, deoarece clienții de e-mail elimină foile de stil. Randarea stă într-o celulă de tabel cu chenar, pe un fundal discret, solicitată la 480 pixeli și afișată la 240, ca să rămână clară pe ecranele retina, cu o etichetă rotunjită deasupra și două rânduri de descriere dedesubt. PNG-ul transparent este esența produsului, așa că îl păstrăm; pe un Outlook vechi care colorează canalul alfa în negru, soluția de rezervă este o randare format: "jpg" pe un fundal de celulă uniform.
<!-- 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>Tot restul mesajului este text: ce este URL-ul, de ce continuă să funcționeze, cererea POST exactă prin care pot genera unul pentru propriul vehicul și un link către această pagină. E-mailul conține un link de dezabonare și antetele List-Unsubscribe, iar fiecare link de produs este etichetat, astfel încât să putem vedea, în raportul de achiziție, dacă povestea pe care o citiți a adus pe cineva înapoi.
Faceți-o chiar dumneavoastră, în trei pași
- Generați pe server, când construiți e-mailul. Un apel per vehicul sau până la cincizeci per apel pentru un rezumat. Stocați
urlșirenews_untilîmpreună cu mesajul. - Puneți URL-ul într-un
<img>cu lățime, înălțime și text alternativ. Textul alternativ numește vehiculul („Porsche 911 2024, roșu, vedere frontală din trei sferturi”), nu „mașină”. - Mențineți contul alimentat. O reînnoire contorizează imaginea o singură dată în luna în care are loc; dacă imaginile de vehicule ale planului sunt epuizate, răspunde cu 402 până când cota se resetează pe 1 ale lunii, așa că dimensionați planul în funcție de imaginile pe care le serviți.
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 itReferința completă a parametrilor, antetele de caching și codurile de eroare se găsesc în documentația despre URL-urile semnate. Abilitatea de agent livrată cu pluginul cunoaște aceleași reguli, astfel încât un asistent căruia i se cere să „pună această mașină în newsletter” generează un URL reînnoibil în loc să lipească o cheie într-un șablon.
Întrebări frecvente
- De ce să nu atașați imaginea sau să o includeți inline ca base64?
- Atașamentele și base64 îngreunează fiecare mesaj. Un URL semnat are câteva sute de octeți și se încarcă prin proxy-ul de imagini al clientului de e-mail, ca orice imagine găzduită.
- Cât costă un URL cu reînnoire automată?
- O imagine de vehicul a lunii la creare, apoi aceeași imagine contorizată o dată în fiecare lună ulterioară în care o fereastră de reînnoire este efectiv deschisă, până la renews_until; o lună în care nimeni nu deschide e-mailul nu contorizează nimic. Pro, la 49 USD pe lună, include 2.500 imagini de vehicul pe lună.
- Ce se întâmplă când imaginile de vehicul ale lunii s-au epuizat?
- Încărcarea răspunde cu 402, iar imaginea apare ca defectă până la resetarea alocării lunare sau până la trecerea la un plan superior; URL-ul în sine rămâne valid și funcționează din nou la următoarea încărcare. O imagine deja contorizată luna aceasta continuă să fie servită.
- Funcționează în Gmail, Outlook și Apple Mail?
- Da. Endpointul de livrare este un simplu GET public cu Cache-Control, un ETag și antete CORS, exact ceea ce așteaptă proxy-urile de imagini. Transparența PNG se afișează corect în toți clienții actuali; pentru versiunile foarte vechi de Outlook, solicitați formatul jpg pe un fundal uniform.
- Poate fi revocat un URL?
- Revocați cheia API care l-a generat și fiecare URL creat cu acea cheie va răspunde cu 403 la următoarea încărcare, inclusiv reînnoirile. Cache-urile partajate continuă să servească imaginea cel mult o oră după aceea.