All articles
2 min read#agents#images#api

Putting a photo on a page when all you have is a URL

A page that loads an image from somebody else’s server is a page with somebody else’s tracking pixel on it.

Why not just accept a URL

Because an image field that takes an address is a hole. Point it at a third-party host and every visitor’s browser announces itself to that host — on a service whose privacy page promises no third-party requests at all. The same field would also flow unescaped into a CSS background, which is its own class of problem.

So image fields accept exactly one shape: a storage key minted by our own upload route. To get one, send the address to the upload endpoint as JSON with a kind of avatar, background or card. The server fetches the file, checks it, re-encodes it and returns a key, which you then save into the page like any other field.

  1. Upload by URL, receive a key, patch the page with that key. Three steps, or one if you pass the photo URL at creation.
  2. Only public addresses are fetched: anything resolving to a private or local address is refused, before and after redirects.
  3. Size caps apply to the source: eight megabytes for avatars and card images, thirty for backgrounds.
  4. The response also carries the stored size and dimensions, which is useful when you want to report what you did.

What happens to the bytes

  • Everything is re-encoded. The file we serve is never the file you sent.
  • EXIF is dropped, which matters because phone photographs carry GPS coordinates.
  • Dimensions and weight are capped per kind, so a page with six images still opens on mobile data.
  • iPhone HEIC files are decoded server-side; you do not need to convert them first.

Anchors, and the mistake to avoid

Templates crop: round for the photo, full-bleed for a background, a strip for a card. Each image field has a companion anchor in percent, and setting it is the difference between a face and a chest. At creation you can pass the anchor with the photo; afterwards it is one more field in an update, and the MCP tool that sets a photo takes it as an argument.

The mistake worth naming: reusing an Instagram CDN address as if it were permanent. Those URLs are signed and expire within days. Copy the bytes across at once — which is exactly what upload-by-URL does — or the avatar breaks quietly a week after you hand the page over.

Read next