Il problema: un'immagine che deve funzionare a marzo
Ogni account su Car Image API riceve un breve messaggio da Jonathan circa un’ora dopo il primo render. La cosa più ovvia da inserire in quell’email è il render stesso: l’esatta Porsche 911 del 2024 o Dodge Charger del 2006 appena richiesta, come prova che l’integrazione funziona e come piccolo vanto.
L'email è la superficie peggiore possibile per un URL firmato. Una pagina viene renderizzata di nuovo a ogni visita; un'email viene renderizzata una sola volta e aperta quando il lettore ci arriva, un giorno, un mese o un anno dopo, spesso tramite il proxy delle immagini di Gmail. Gli URL di consegna firmati già offerti dall'API avevano un TTL massimo di sette giorni: il limite giusto per un browser, ma che garantisce un'immagine non visualizzata nella casella di posta entro la seconda settimana. Allegare o incorporare il PNG appesantisce ogni messaggio.
Volevamo un URL che si comportasse come un'immagine ospitata, continuasse a funzionare finché qualcuno avesse potuto aprire l'email, e addebitasse comunque all'account in modo equo ciò che effettivamente fornisce.
La soluzione: URL firmati a rinnovo automatico
Abbiamo aggiunto un solo flag a POST /api/v1/image-urls: renew: true. Un URL rinnovabile viene generato esattamente come qualsiasi altro, conta la sua immagine come immagine di veicolo del mese ed è valido per ttl_seconds. La differenza sta in ciò che accade dopo il TTL. Invece di un 403, il primo caricamento in ogni nuova finestra di ttl_seconds rinnova l’URL e serve l’immagine; un rinnovo conta l’immagine una volta nel mese in cui avviene, e nulla di più se era già stata conteggiata quel mese. Ogni caricamento successivo in quella finestra è memorizzabile nella cache pubblica, come nella prima finestra. Questo continua fino a renews_until, fino a un anno dopo.
Da quel design sono emerse tre proprietà, e ognuna era importante per l'email:
- Niente da reinviare o rigenerare. L’URL nell’email resta lo stesso per sempre. Nessun cron job che riscrive i link salvati, nessuna seconda email con un’immagine nuova.
- Si contano le aperture, non il tempo. Una finestra che nessuno apre non conta nulla. Un’email letta da una persona in un solo giorno equivale all’unica immagine di veicolo per cui è stata generata; una aperta ogni settimana per un anno conta quell’immagine una volta in ogni mese in cui viene aperta, al massimo dodici volte.
- Conteggio idempotente. Ogni finestra viene registrata una sola volta, con chiave
renew:<url id>:<window>, univoca per URL e finestra. Cinquanta aperture in una settimana, o due proxy di posta che competono alla prima apertura, contano una volta sola.
Le email di ciclo di vita generano questi URL tramite lo stesso percorso di codice usato dall'endpoint pubblico, come token di piattaforma: non legati ad alcuna chiave API e mai fatturati, perché sono nostri, ma serviti dalla stessa route GET /api/v1/delivery/{token}, con le stesse finestre di sette giorni e la stessa cache. L'immagine nell'email del primo render è il prodotto che dimostra se stesso.
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 (sette giorni, il massimo) significa al massimo un rinnovo a settimana per immagine, e l’immagine viene conteggiata al massimo una volta al mese per quanto spesso si rinnovi. Funziona anche un TTL di un’ora con renew: true, ma una casella di posta molto usata rinnoverebbe ogni ora in cui viene aperta.L'email vera e propria
Il template è realizzato con React Email e stili inline, perché i client di posta rimuovono i fogli di stile. Il render si trova in una cella di tabella con bordo su uno sfondo tenue, richiesto a 480 pixel e mostrato a 240 così resta nitido sugli schermi retina, con un’etichetta a pillola sopra e due righe di didascalia sotto. Il PNG trasparente è il senso stesso del prodotto, quindi lo manteniamo; sulle vecchie versioni di Outlook che rendono nero il canale alfa, la soluzione di riserva è un render format: "jpg" su uno sfondo di cella a tinta unita.
<!-- 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>Tutto il resto del messaggio è testo: che cos’è l’URL, perché continua a funzionare, la POST esatta per generarne uno per il proprio veicolo e un link a questa pagina. L’email contiene un link per annullare l’iscrizione e le intestazioni List-Unsubscribe, e ogni link al prodotto è tracciato, così nel report di acquisizione possiamo vedere se la storia che sta leggendo ha riportato qualcuno da noi.
Come farlo in autonomia in tre passaggi
- Generi gli URL lato server, quando crea l’email. Una chiamata per veicolo, oppure fino a cinquanta per chiamata per un riepilogo. Salvi
urlerenews_untilinsieme al messaggio. - Inserisca l'URL in un
<img>con larghezza, altezza e testo alternativo. Il testo alternativo indica il veicolo ("Porsche 911 2024, rossa, vista anteriore di tre quarti"), non "auto". - Mantenga l'account attivo. Un rinnovo conta l'immagine una volta nel mese in cui avviene; se le immagini di veicoli del piano sono esaurite risponde 402 finché la quota non si azzera il 1° del mese, quindi dimensioni il piano in base alle immagini che 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 itIl riferimento completo dei parametri, gli header di cache e i codici di errore si trovano nella documentazione sugli URL firmati. La skill per agenti inclusa nel plugin conosce le stesse regole, quindi un assistente a cui si chiede di "mettere quest'auto nella newsletter" genera un URL rinnovabile invece di incollare una chiave in un template.
Domande frequenti
- Perché non allegare l'immagine o incorporarla in base64?
- Allegati e base64 appesantiscono ogni messaggio. Un URL firmato occupa poche centinaia di byte e si carica tramite il proxy delle immagini del client di posta, come qualsiasi immagine ospitata online.
- Quanto costa un URL a rinnovo automatico?
- Un'immagine di veicolo del mese alla creazione, poi la stessa immagine conteggiata una volta in ogni mese successivo in cui una finestra di rinnovo viene effettivamente aperta, fino a renews_until; un mese in cui nessuno apre l'email non conta nulla. Pro, a 49 USD al mese, include 2500 immagini di veicoli al mese.
- Cosa succede quando le immagini di veicoli del mese sono esaurite?
- Il caricamento risponde 402 e l'immagine appare non disponibile finché la quota del mese non si azzera o il piano non viene aumentato; l'URL in sé resta valido e riprende a funzionare al caricamento successivo. Un'immagine già conteggiata questo mese continua a essere servita.
- Funziona in Gmail, Outlook e Apple Mail?
- Sì. L’endpoint di consegna è una semplice GET pubblica con intestazioni Cache-Control, ETag e CORS, esattamente ciò che si aspettano i proxy di immagini. La trasparenza PNG viene visualizzata in tutti i client attuali; per le versioni molto vecchie di Outlook, richieda il formato jpg su sfondo pieno.
- È possibile revocare un URL?
- Revochi la chiave API che lo ha generato e ogni URL di quella chiave risponderà 403 al caricamento successivo, rinnovi inclusi. Le cache condivise continuano a servirlo al massimo per un'ora dopo la revoca.