Media storage

Attachments are uploaded by the browser or app directly to storage, with short-lived signed URLs from ByoTalk. Without your own storage, files go to ours. Connect yours (Storage → Media) and files go directly to your own account; we keep only the object key and metadata.

Supported providers:

Provider What you need
Cloudinary cloudinary://<api_key>:<api_secret>@<cloud_name> and an optional folder
S3-compatible Bucket, region, access key pair; endpoint for non-AWS providers
Azure Blob Storage Storage account, account key, container

Secrets are stored encrypted and never shown again after saving. Saving runs a test: upload, read, delete and, when you give your app's origin, CORS (can browsers at that origin upload?).

S3-compatible presets

Preset Endpoint Region
Amazon S3 leave empty the bucket's region, e.g. eu-central-1
Cloudflare R2 https://<accountid>.r2.cloudflarestorage.com auto
Google Cloud Storage leave empty (https://storage.googleapis.com) auto; use HMAC keys (Settings → Interoperability)
DigitalOcean Spaces leave empty e.g. nyc3
Wasabi leave empty e.g. us-east-1
Backblaze B2 leave empty e.g. us-west-004
MinIO / other required, e.g. https://minio.example.com:9000 usually us-east-1; path-style URLs

Give the key pair access to this bucket only: PutObject, GetObject and DeleteObject.

CORS rules

Browsers upload with PUT from your app's origin, so the bucket needs a CORS rule.

S3-compatible (JSON form, most providers):

[
  {
    "AllowedOrigins": ["https://app.example.com"],
    "AllowedMethods": ["PUT", "GET"],
    "AllowedHeaders": ["content-type"],
    "MaxAgeSeconds": 3600
  }
]

S3-compatible (XML form):

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>https://app.example.com</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedHeader>content-type</AllowedHeader>
    <MaxAgeSeconds>3600</MaxAgeSeconds>
  </CORSRule>
</CORSConfiguration>

Azure Blob Storage: portal → storage account → Settings → Resource sharing (CORS) → Blob service:

Field Value
Allowed origins https://app.example.com
Allowed methods PUT, GET
Allowed headers content-type,x-ms-blob-type
Exposed headers *
Max age 3600

Cloudinary uploads use signed requests and need no CORS rule.

Switching back

Remove in the dashboard returns the environment to Managed media. Files already uploaded stay where they are.