Car Image API

Klanten · Car Image API

Auto-afbeeldingen in e-mail insluiten met signed URLs die nooit breken

Car Image API zet de eerste render van elke klant in diens inbox met een automatisch verlengende ondertekende URL: het mechanisme, de berekening en de code om het zelf te doen.

Het probleem: een afbeelding die in maart moet werken

Elk account op Car Image API krijgt ongeveer een uur na de eerste render een kort bericht van Jonathan. Het meest voor de hand liggende om in die e-mail te zetten is de render zelf: precies de 2024 Porsche 911 of 2006 Dodge Charger waar de persoon net om vroeg, als bewijs dat de integratie werkt en als klein beetje opschepperij.

E-mail is de slechtst denkbare plek voor een ondertekende URL. Een pagina wordt bij elk bezoek opnieuw gerenderd; een e-mail wordt één keer gerenderd en geopend wanneer de lezer eraan toekomt, een dag, een maand of een jaar later, vaak via de afbeeldingsproxy van Gmail. De ondertekende bezorg-URL's die de API al aanbood, hadden een maximale TTL van zeven dagen: het juiste plafond voor een browser, maar een gegarandeerd kapotte afbeelding in een inbox vanaf week twee. De PNG als bijlage of inline meesturen maakt elk bericht onnodig zwaar.

Wat we wilden, was een URL die zich gedraagt als een gehoste afbeelding, blijft werken zolang iemand de e-mail zou kunnen openen, en het account toch eerlijk laat betalen voor wat er daadwerkelijk wordt geleverd.

De oplossing: automatisch vernieuwende ondertekende URL's

We hebben één vlag toegevoegd aan POST /api/v1/image-urls: renew: true. Een vernieuwbare URL wordt precies zo aangemaakt als elke andere, telt zijn afbeelding als voertuigafbeelding van de maand en is geldig gedurende ttl_seconds. Het verschil zit in wat er na de TTL gebeurt. In plaats van een 403 vernieuwt de eerste keer laden in elk nieuw venster van ttl_seconds de URL en levert die de afbeelding; een vernieuwing telt de afbeelding één keer in de maand waarin die plaatsvindt, en verder niets als de afbeelding die maand al was geteld. Elke volgende keer laden in dat venster is openbaar cachebaar, net als in het eerste venster. Dat gaat door tot renews_until, maximaal een jaar vooruit.

Uit dat ontwerp volgden drie eigenschappen, en elk ervan was belangrijk voor e-mail:

  • Niets opnieuw te versturen of aan te maken. De URL in de e-mail blijft voor altijd dezelfde URL. Geen cronjob die opgeslagen links herschrijft, geen tweede e-mail met een nieuwe afbeelding.
  • Tel openingen, geen tijd. Een venster waarin niemand de e-mail opent, telt niets. Een e-mail die door één persoon op één dag wordt gelezen, is de ene voertuigafbeelding waarvoor hij is aangemaakt; een e-mail die een jaar lang elke week wordt geopend, telt die afbeelding één keer in elke maand waarin hij wordt geopend, hooguit twaalf keer.
  • Idempotent tellen. Elk venster wordt één keer vastgelegd, met sleutel renew:<url id>:<window>, uniek per URL en venster. Vijftig keer openen in een week, of twee mailproxy’s die tegelijk de eerste opening verwerken, tellen één keer.

De lifecycle-e-mails maken deze URL's aan via hetzelfde codepad als het openbare endpoint, als platformtokens: aan geen enkele API-sleutel gekoppeld en nooit gefactureerd, omdat ze van ons zijn, maar geleverd via exact dezelfde route GET /api/v1/delivery/{token}, met dezelfde vensters van zeven dagen en dezelfde caching. De afbeelding in de e-mail over de eerste render is het product dat zichzelf demonstreert.

Maak één vernieuwbare URL aan voor de eerste render van de klant
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…"
}

De e-mail zelf

De template is React Email met inline styles, omdat mailclients stylesheets verwijderen. De render staat in een tabelcel met rand op een zachte achtergrond, opgevraagd op 480 pixels en getoond op 240, zodat hij scherp blijft op retina-schermen, met een label erboven en twee bijschriftregels eronder. Transparante PNG is de kern van het product, dus die houden we; in een oude Outlook die alfa als zwart weergeeft, is een render met format: "jpg" op een effen celachtergrond de fallback.

De kaart, als gewone HTML
<!-- 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>

Al het andere in het bericht is tekst: wat de URL is, waarom die blijft werken, de exacte POST om er een aan te maken voor hun eigen voertuig, en een link naar deze pagina. De e-mail bevat een afmeldlink en de List-Unsubscribe-headers, en elke productlink is getagd, zodat we in het acquisitierapport kunnen zien of het verhaal dat u nu leest iemand heeft teruggebracht.

Zelf aan de slag in drie stappen

  1. Maak de URL's server-side aan, wanneer u de e-mail opbouwt. Eén aanroep per voertuig, of maximaal vijftig per aanroep voor een overzichtsmail. Sla url en renews_until op bij het bericht.
  2. Plaats de URL in een <img> met breedte, hoogte en alt-tekst. De alt-tekst noemt het voertuig ("2024 Porsche 911, rood, driekwartaanzicht van voren"), niet "auto".
  3. Houd het account op peil. Een verlenging telt de afbeelding eenmaal in de maand waarin die valt; als de voertuigafbeeldingen van het abonnement op zijn, antwoordt de URL met 402 totdat het tegoed op de 1e wordt gereset, dus stem het abonnement af op het aantal afbeeldingen dat u levert.
Met de 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

De volledige parameterreferentie, cachingheaders en foutcodes staan in de documentatie over signed URL's. De agentskill die met de plug-in wordt meegeleverd, kent dezelfde regels, zodat een assistent die wordt gevraagd om "deze auto in de nieuwsbrief te zetten" een vernieuwbare URL aanmaakt in plaats van een sleutel in een template te plakken.

Veelgestelde vragen

Waarom de afbeelding niet als bijlage toevoegen of inline als base64 opnemen?
Bijlagen en base64 maken elk bericht onnodig groot. Een ondertekende URL is een paar honderd bytes en laadt via de afbeeldingsproxy van de mailclient zoals elke gehoste afbeelding.
Wat kost een automatisch vernieuwende URL?
Eén voertuigafbeelding van de maand bij het aanmaken, daarna dezelfde afbeelding één keer geteld in elke volgende maand waarin een verlengingsvenster daadwerkelijk wordt geopend, tot renews_until; een maand waarin niemand de e-mail opent, telt niets. Pro, voor US$ 49 per maand, bevat 2.500 voertuigafbeeldingen per maand.
Wat gebeurt er als de voertuigafbeeldingen van de maand op zijn?
Het laden geeft 402 terug en de afbeelding wordt als kapot weergegeven totdat het maandtegoed opnieuw begint of het abonnement wordt verhoogd; de URL zelf blijft geldig en werkt weer bij de volgende keer laden. Een afbeelding die deze maand al is geteld, blijft gewoon beschikbaar.
Werkt dit in Gmail, Outlook en Apple Mail?
Ja. Het delivery-endpoint is een gewone openbare GET met Cache-Control-, ETag- en CORS-headers, precies wat beeldproxy's verwachten. PNG-transparantie wordt in elke huidige client weergegeven; vraag voor zeer oude Outlook-versies format jpg aan op een effen achtergrond.
Kan een URL worden ingetrokken?
Trek de API-sleutel in waarmee de URL is aangemaakt, en elke URL van die sleutel geeft bij de volgende keer laden 403, ook na vernieuwing. Gedeelde caches blijven daarna nog maximaal een uur serveren.