Skip to content
LogoLogo

Attachments

Direct-to-object-storage file uploads and downloads.

Reserve a direct upload

POST/api/v1/attachment-uploads

Atomically reserves the declared byte count against the Workspace's pooled storage entitlement and returns a short-lived presigned URL bound to one method, one object key, and the reserved length. File bytes never pass through the Assign API.

The reservation expires if it is not completed. Expired, cancelled, and abandoned reservations release their quota.

Associating a completed attachment with a parent Task, comment, or Document is deliberately not part of this contract: attachment parent cardinality is an open decision and will be contracted with it rather than inferred from an implementation.

Header Parameters

X-CSRF-Token
string·min 1·max 200

Required when this operation uses browserSession; native bearer callers omit it because no ambient cookie is sent.

Idempotency-KeyRequired
string·min 16·max 200

Opaque client-generated key for safely retrying this operation.

Request Body

application/json
workspace_idRequired
string <uuid>

Opaque Assign-owned UUIDv7 identifier. Clients must not derive meaning from it.

Example0198ac5e-f9b7-7000-8000-000000000001
filenameRequired
string·min 1·max 255

Display filename, at most 255 UTF-8 bytes after NFC normalization. It must not contain a path separator, null, or control character, must not be . or .., and must not begin or end with whitespace or a dot. A violating filename is rejected rather than rewritten. Filenames never contribute to the server-generated object key.

content_lengthRequired
integer·min 1·max 52428800

Exact byte count to reserve. The free tier allows 50 MiB per object.

content_type
string·max 255

Advisory only. The stored content type is detected from the object's leading bytes at completion and may differ.

sha256
string

Lowercase hexadecimal SHA-256 digest.

Responses

Complete a reserved upload

POST/api/v1/attachment-uploads/{upload_id}/complete

Verifies the uploaded object, streams it through the private malware scanner, and converts reserved bytes into stored bytes. Completion checks object existence, the exact observed size, the expected object key, and the SHA-256 digest, and decides the content type from the object's leading bytes — never from the client-declared type, which is advisory and is discarded.

Detection and the filename extension are independent grounds for denial: an object whose detected type is on the execution-risk denylist is rejected whatever it is named, and a denied extension is rejected whatever the bytes contain. Neither can permit what the other rejects. SVG is retained as a download-only attachment: it is never eligible for inline display. Scanner failures fail completion closed. A detected malicious attachment is retained only in private quarantine with scan_state: quarantined; it cannot be linked, previewed, or downloaded.

Path Parameters

upload_idRequired
string <uuid>
Example0198ac5e-f9b7-7000-8000-000000000001

Header Parameters

X-CSRF-Token
string·min 1·max 200

Required when this operation uses browserSession; native bearer callers omit it because no ambient cookie is sent.

Request Body

application/json
sha256
string

Lowercase hexadecimal SHA-256 digest.

Responses

Cancel a reserved upload

DELETE/api/v1/attachment-uploads/{upload_id}

Releases the reservation and its quota. The operation is idempotent and succeeds for an already-cancelled or already-expired reservation.

Path Parameters

upload_idRequired
string <uuid>
Example0198ac5e-f9b7-7000-8000-000000000001

Header Parameters

X-CSRF-Token
string·min 1·max 200

Required when this operation uses browserSession; native bearer callers omit it because no ambient cookie is sent.

Responses

Read attachment metadata

GET/api/v1/attachments/{attachment_id}

Path Parameters

attachment_idRequired
string <uuid>
Example0198ac5e-f9b7-7000-8000-000000000001

Responses

Delete an attachment

DELETE/api/v1/attachments/{attachment_id}

Soft-deletes the attachment. Stored bytes continue to count against the storage entitlement until the object is permanently purged at the end of the retention window; deletion is never triggered automatically by quota exhaustion.

Path Parameters

attachment_idRequired
string <uuid>
Example0198ac5e-f9b7-7000-8000-000000000001

Header Parameters

X-CSRF-Token
string·min 1·max 200

Required when this operation uses browserSession; native bearer callers omit it because no ambient cookie is sent.

Responses

Authorize a direct download

POST/api/v1/attachments/{attachment_id}/download

Authorizes the caller against the attachment's Workspace at request time and mints a short-lived presigned download URL bound to one method and one object key. Possession of an attachment identifier or an expired URL never grants access.

The URL is minted per request and must never be stored in Document or comment content, an event payload, a notification, or a webhook. The object is served from a non-Assign origin with Content-Disposition: attachment and X-Content-Type-Options: nosniff; attachments are never rendered inline in an Assign origin.

Only an attachment whose scan_state is clean can receive this credential. This operation is POST because it mints a credential; it is not a cacheable read.

Path Parameters

attachment_idRequired
string <uuid>
Example0198ac5e-f9b7-7000-8000-000000000001

Header Parameters

X-CSRF-Token
string·min 1·max 200

Required when this operation uses browserSession; native bearer callers omit it because no ambient cookie is sent.

Responses

Authorize an inline attachment preview

POST/api/v1/attachments/{attachment_id}/preview

Authorizes the caller against the attachment's Workspace at request time and mints a short-lived presigned URL for browser display. The object is served from the cookie-less object-storage origin with an inline disposition, X-Content-Type-Options: nosniff, and the server-detected content type.

Only clean PNG, JPEG, GIF, WebP, AVIF, and PDF attachments are previewable. SVG and every other type remain download-only. The URL is a credential, is minted per request, and must never be stored in content, events, notifications, or webhooks.

This operation is POST because it mints a credential; it is not a cacheable read.

Path Parameters

attachment_idRequired
string <uuid>
Example0198ac5e-f9b7-7000-8000-000000000001

Header Parameters

X-CSRF-Token
string·min 1·max 200

Required when this operation uses browserSession; native bearer callers omit it because no ambient cookie is sent.

Responses