Skip to main content

Attachments

This page explains how attachments work, storage rules, validation and ways to include them in messages.

Ways to Add Attachments

MethodEndpointUse CaseNotes
Inline URL shortcutPOST /api/v1/messagesQuick add of a single media via imageUrl / videoUrl / gifUrlCreates transient attachment objects (not persisted if saveOnServer=false).
JSON attachments arrayPOST /api/v1/messagesMultiple attachments or fine-grained controlProvide objects with mediaType + url OR attachmentUuid.
Uploaded file (multipart)POST /api/v1/messages/with-attachmentDirect file uploadReturns a message with linked stored attachment.
Reuse stored filePOST /api/v1/messagesReference existing attachmentUuidAvoids re-upload / re-download.

Attachments API Reference

Use these endpoints when you want to upload or register files ahead of time and later reference them by attachmentUuid inside a message attachments array.

PurposeMethod & PathRefNotes
Upload file (multipart)POST /api/v1/attachments/uploadAPI ↗Form field: file; optional filename, mediaType (auto-detected if omitted).
Download & persist from URLPOST /api/v1/attachments/download-from-urlAPI ↗Server fetches remote file; optional filename, mediaType.
Get one (metadata)GET /api/v1/attachments/{id}API ↗Returns metadata (auth required).
Download (auth)GET /api/v1/attachments/{id}/downloadAPI ↗Binary stream (requires auth).
Download (public)GET /api/v1/attachments/{id}/download/publicAPI ↗Only for public attachments (linked to message or bucket ICON).
List by messageGET /api/v1/attachments/message/{messageId}API ↗Inspect attachments after message creation.
DeleteDELETE /api/v1/attachments/{id}API ↗Permanently removes attachment (breaks references).

Auth: All endpoints require Authorization: Bearer <jwt-access-token> or an access token (zat_...) except the explicit public download.

Upload Example

curl -X POST http://localhost:3001/api/v1/attachments/upload \
-H "Authorization: Bearer <jwt-access-token>" \
-F "file=@banner.png" \
-F "filename=banner.png" \
-F "mediaType=IMAGE"

Response (excerpt):

{
"id": "<attachment-uuid>",
"filename": "banner.png",
"mediaType": "IMAGE"
}

Download & Persist From Remote URL

curl -X POST http://localhost:3001/api/v1/attachments/download-from-url \
-H "Authorization: Bearer <jwt-access-token>" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/image.png","filename":"promo.png","mediaType":"IMAGE"}'

Reference in a Message

{
"title": "Promo Available",
"bucketId": "<bucket-uuid>",
"deliveryType": "NORMAL",
"attachments": [
{ "attachmentUuid": "<attachment-uuid>", "mediaType": "IMAGE" }
]
}

Notes

  • If both url and attachmentUuid are provided in a single attachment object, attachmentUuid wins.
  • To persist a remote file inline (without pre-upload) you can use attachments[].url + saveOnServer=true directly in the message body.
  • Public download succeeds only for attachments considered public (linked to a message or bucket icon). Otherwise use the authenticated download endpoint.

Attachment Object

Either url or attachmentUuid must be provided (mutually exclusive).

FieldTypeRequiredDescription
mediaTypeenum MediaTypeYesOne of: VIDEO, IMAGE, GIF, AUDIO, ICON (ICON also fallback for generic/docs).
namestringNoFriendly display name (filename).
urlstringConditionalRemote URL to fetch/display. Can't be used with attachmentUuid.
attachmentUuidstringConditionalExisting stored attachment ID. Can't be used with url.
saveOnServerbooleanNoIf true with url, server downloads and persists a copy (requires attachments enabled).

Validation errors occur if both or neither of url and attachmentUuid are supplied.

Storage Layout

Files are persisted under a per-user, per-media-type, per-attachment folder structure:

/attachments/<userId>/<mediaType>/<attachmentId>/<attachmentId>.<ext>

Bucket icons may automatically be injected as an ICON attachment if present and not duplicated.

File Size & Types

Defaults (overridable by env):

  • Max size: ATTACHMENTS_MAX_FILE_SIZE (default 10MB)
  • Allowed MIME types: ATTACHMENTS_ALLOWED_MIME_TYPES (image/jpeg, image/png, image/gif, image/webp, video/mp4, video/webm, audio/mpeg, audio/wav, audio/ogg, application/pdf, text/plain)

If the MIME type or size fails validation a 400 is returned.

Public Access

Only attachments linked to a message OR bucket icon type (ICON) are publicly accessible through any public endpoint (if exposed). Others remain private.

Automatic URL Shortcuts

Shortcuts convert single URLs into implicit attachments:

  • imageUrl => IMAGE attachment
  • videoUrl => VIDEO attachment
  • gifUrl => GIF attachment

These are merged with explicitly provided attachments if both forms are used.

Download & Persist Remote URL

Set saveOnServer: true on a URL-based attachment to have the server fetch and store the file, replacing url with a generated attachmentUuid.

Reusing Attachments

To reuse a file in multiple messages:

  1. Upload once (multipart or URL + saveOnServer)
  2. Reference via attachmentUuid in subsequent message payloads

Cleanup

A background cleanup job can remove old attachments (excluding ICON) based on retention settings. Ensure you don't rely on very old unreferenced files.

Example Payload (Mixed)

{
"title": "Release Media",
"bucketId": "<bucket-uuid>",
"deliveryType": "NORMAL",
"attachments": [
{ "mediaType": "IMAGE", "url": "https://example.com/banner.png", "saveOnServer": true },
{ "mediaType": "AUDIO", "attachmentUuid": "<existing-audio-uuid>", "name": "Theme" }
],
"videoUrl": "https://cdn.example.com/teaser.mp4"
}

Troubleshooting

SymptomLikely CauseFix
400 both url & attachmentUuidProvided both fieldsRemove one of them
400 invalid mediaTypeTypo or unsupported valueUse an enum value exactly as listed
400 file size exceedsFile > max sizeIncrease env limit or compress file
Attachment not visibleNot linked to message and not ICONEnsure message linkage or mediaType ICON
Download timeoutRemote host slow/unreachableHost file on faster CDN or retry

Return to main notifications: Notifications Overview