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.