Custom file storage

Content & assets

Custom file storage

Keep uploads on your own infrastructure, no cloud secret ever reaches the browser.

  • Private S3 connector, { mode: "private", listEndpoint } on your backend lists + presigns.
  • Upload handler, onImageUpload(file) stores the file on your CDN and returns a URL.
  • Public bucket, a region/bucket config for a simple public store.

See also Amazon S3 and image uploads.

Private bucket: the endpoint contract

With { mode: "private", listEndpoint } the editor sends a GET to listEndpoint and shows what comes back. Return { items: [...] } or a bare array. Each item needs a url; key, name or label, thumbnailUrl, kind, alt and size are optional.

json
{ "items": [ { "key": "campaigns/hero.png", "url": "https://assets.example.com/campaigns/hero.png?X-Amz-Signature=…", "kind": "image" } ] }

The request carries the user's cookies (credentials: "include") and any headers you set, so your backend can limit the list to the signed-in organization. On a non-2xx response the editor shows the message field of the JSON body as the error.

Presigned URLs expire

Presigned S3 URLs last at most 7 days. Re-resolve them at send time, or serve the objects from a public or CDN address (see asset domain), so an email sent later does not show broken images.

Upload handler

onImageUpload(file, { kind }) must resolve to { url, alt? }. Without it, uploads are stored in the design as base64 data: URLs, which make the email much larger and which many email clients strip. The File block will not upload at all without a handler.