Problemet: et bilde som må fungere i mars
Hver konto på Car Image API får en kort melding fra Jonathan omtrent en time etter sin første rendering. Det opplagte å legge i den e-posten er selve renderingen: nøyaktig den 2024 Porsche 911 eller 2006 Dodge Charger personen nettopp ba om, som bevis på at integrasjonen fungerte, og som et lite skryt.
E-post er den verst tenkelige flaten for en signert URL. En nettside gjengis på nytt ved hvert besøk; en e-post gjengis én gang og åpnes når leseren kommer til den, en dag, en måned eller et år senere, ofte gjennom Gmails bildeproxy. De signerte leverings-URL-ene API-et allerede tilbød, hadde en maksimal TTL på sju dager, noe som er riktig tak for en nettleser, men som garanterer et ødelagt bilde i innboksen innen uke to. Å legge ved eller bygge inn PNG-en gjør hver e-post unødvendig stor.
Det vi ønsket, var en URL som oppfører seg som et hostet bilde, fortsetter å virke så lenge noen kan tenkes å åpne e-posten, og likevel belaster kontoen rettferdig for det den faktisk leverer.
Løsningen: signerte URL-er som fornyes automatisk
Vi la til ett flagg i POST /api/v1/image-urls: renew: true. En fornybar URL genereres akkurat som alle andre, teller bildet sitt som et kjøretøybilde for måneden og er gyldig i ttl_seconds. Forskjellen er hva som skjer etter TTL-en. I stedet for en 403 fornyer den første innlastingen i hvert nye vindu på ttl_seconds URL-en og leverer bildet. En fornyelse teller bildet én gang i måneden den skjer, og ingenting mer hvis bildet allerede er telt den måneden. Alle videre innlastinger i det vinduet kan mellomlagres offentlig, akkurat som i det første vinduet. Slik fortsetter det frem til renews_until, opptil ett år frem i tid.
Tre egenskaper fulgte av den utformingen, og hver av dem var viktig for e-post:
- Ingenting å sende eller generere på nytt. URL-en i e-posten er URL-en for alltid. Ingen cron-jobb som skriver om lagrede lenker, ingen ny e-post med et ferskt bilde.
- Tell åpninger, ikke tid. Et vindu som ingen åpner, teller ingenting. En e-post som leses av én person på én dag, tilsvarer det ene kjøretøybildet den ble generert for. En e-post som åpnes hver uke i et år, teller bildet én gang i hver måned den åpnes, maks tolv ganger.
- Idempotent telling. Hvert vindu registreres én gang, med nøkkelen
renew:<url id>:<window>, unik per URL og vindu. Femti åpninger på en uke, eller to e-postproxyer som kappes om den første åpningen, telles én gang.
Livssyklus-e-postene lager disse URL-ene gjennom samme kodesti som det offentlige endepunktet bruker, som plattformtokener: knyttet til ingen API-nøkkel og aldri fakturert, fordi de er våre, men levert av den samme GET /api/v1/delivery/{token}-ruten, med de samme sjudagersvinduene og den samme hurtigbufringen. Bildet i e-posten om første rendering er produktet som demonstrerer seg selv.
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 (sju dager, maksimum) betyr maks én fornyelse i uken per bilde, og bildet telles maks én gang i måneden uansett hvor ofte det fornyes. En TTL på én time med renew: true fungerer også, men i en travel innboks ville URL-en blitt fornyet hver time e-posten åpnes.Selve e-posten
Malen er laget med React Email med innebygde stiler, fordi e-postklienter fjerner stilark. Renderingen ligger i en tabellcelle med kantlinje på en dempet bakgrunn, hentet i 480 piksler og vist i 240 slik at den holder seg skarp på retina-skjermer, med en etikett over og to bildetekstlinjer under. Transparent PNG er hele poenget med produktet, så vi beholder det; i en gammel Outlook som gjør alfakanalen svart, er en format: "jpg"-rendering på en ensfarget cellebakgrunn reserveløsningen.
<!-- 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>Alt annet i meldingen er tekst: hva URL-en er, hvorfor den fortsetter å virke, nøyaktig hvilken POST som lager en for deres eget kjøretøy, og en lenke til denne siden. E-posten har en avmeldingslenke og List-Unsubscribe-headerne, og hver produktlenke er merket slik at vi i anskaffelsesrapporten kan se om historien du leser, har fått noen til å komme tilbake.
Gjør det selv i tre steg
- Generer på serversiden når du bygger e-posten. Ett kall per kjøretøy, eller opptil femti per kall for en oppsummering. Lagre
urlogrenews_untilsammen med meldingen. - Legg URL-en i en
<img>med bredde, høyde og alt-tekst. Alt-teksten navngir kjøretøyet («2024 Porsche 911, rød, skrått forfra»), ikke «bil». - Sørg for at kontoen har dekning. En fornyelse teller bildet én gang i måneden den faller i. Hvis planens kjøretøybilder er brukt opp, svarer den 402 til kvoten nullstilles den 1., så tilpass planen til antall bilder du leverer.
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 itDen fullstendige parameterreferansen, hurtigbufferhoder og feilkoder finner du i dokumentasjonen for signerte URL-er. Agentferdigheten som følger med pluginen, kjenner de samme reglene, så en assistent som blir bedt om å «legge denne bilen i nyhetsbrevet», lager en fornybar URL i stedet for å lime inn en nøkkel i en mal.
Ofte stilte spørsmål
- Hvorfor ikke legge ved bildet eller bygge det inn som base64?
- Vedlegg og base64 gjør hver melding unødvendig stor. En signert URL er noen hundre byte og lastes gjennom e-postklientens bildeproxy som et hvilket som helst annet nettbasert bilde.
- Hva koster en URL som fornyes automatisk?
- Ett av månedens kjøretøybilder når det opprettes, deretter telles det samme bildet én gang i hver senere måned der et fornyelsesvindu faktisk åpnes, frem til renews_until; en måned der ingen åpner e-posten, teller ingenting. Pro, til 49 USD i måneden, inkluderer 2 500 kjøretøybilder i måneden.
- Hva skjer når månedens kjøretøybilder er brukt opp?
- Innlastingen svarer 402, og bildet vises som ødelagt til månedens kvote nullstilles eller planen oppgraderes; selve URL-en forblir gyldig og fungerer igjen ved neste innlasting. Et bilde som allerede er telt denne måneden, vises fortsatt.
- Fungerer dette i Gmail, Outlook og Apple Mail?
- Ja. Leveringsendepunktet er en vanlig offentlig GET med Cache-Control, en ETag og CORS-headere, som er nøyaktig det bildeproxyer forventer. PNG-gjennomsiktighet vises i alle nåværende klienter; for svært gamle Outlook-versjoner bør du be om format jpg på ensfarget bakgrunn.
- Kan en URL tilbakekalles?
- Tilbakekall API-nøkkelen som genererte den, så svarer alle URL-er fra den nøkkelen med 403 ved neste innlasting, fornyelser inkludert. Delte hurtigbuffere fortsetter å levere i maks én time etter det.