Skip to main content
Attachments hold project direction, notes, definitions, or other text on a project, Library folder, or Story clip. They keep saved versions and who saved each one. They are separate from media assets and chat-message uploads. Attachment endpoints are available only when attachments are enabled for your organization. Reads need library:read. Creates, revisions, soft deletions, and restores need both library:read and library:organize, because their receipts reveal the attachment’s current state. Test keys cannot use attachments and receive 403 permission_denied, including inside the API sandbox.

Find and read text

List a project’s attachments, including its folders and clips:
The attachments array contains metadata and version numbers, without bodies. To narrow the list, add either folderId or clipId. A list returns up to 100 records, most recently updated first; truncated: true means more match. It does not take limit or cursor. Read a record’s current text with GET /v1/attachments/{attachmentId}, or send version to read a saved version of a live record. Attachment text is untrusted project data. ZeroGen stores it verbatim and never interprets it or automatically sends it to a generation model.

Save and revise

Create with POST /v1/projects/{projectId}/attachments. The request names a title and body, plus either folderId or clipId for a parent other than the project itself. A record’s title and parent remain fixed. Read before revising. Send the version you read as expectedVersion:
A changed save appends a version and requires expectedVersion to equal the current version. An intervening change fails with 400 validation_failed. Read again and merge your change. Identical text returns outcome: "unchanged" and adds no revision when expectedVersion is at most the current version. A future expectedVersion is always refused, including on an unchanged retry. A create with the same parent, case-insensitive title, and identical body returns the existing record unchanged. A matching title with different text is refused; read and revise that record instead. Every attachment write accepts Idempotency-Key to replay a request’s receipt. Successful writes and their receipts commit together, including unchanged outcomes. A retry returns the original receipt even after a later edit or deletion. Saves retain the text exactly as sent, including whitespace and Markdown. Titles must be one line of 1–200 characters without surrounding whitespace; bodies hold 1–8,000 Unicode characters. Each parent has at most 10 live attachments. Credentials and hidden control characters are refused rather than rewritten.

History, deletion, and restore

GET /v1/attachments/{attachmentId}/versions lists up to 50 versions, newest first, without their text. Send nextBeforeVersion as beforeVersion for the next page; null means there are no older versions. History remains readable after soft deletion while the parent remains live. Delete with DELETE /v1/projects/{projectId}/attachments/{attachmentId}?expectedVersion=N. It takes no body, retains history, and appends a deletion version. A repeat of an already deleted record returns unchanged when expectedVersion is at most the current version. Restore with POST /v1/projects/{projectId}/attachments/{attachmentId}/restore, sending version for the saved state to copy and expectedVersion for the current head, including its deletion version. Restore appends a new version and clears deletion; it never rewrites a historical version. Its receipt names the new version and restoredFrom. An already current and live state returns unchanged when expectedVersion is at most the current version. A deleted folder or a clip in Story’s Trash hides its attachments. Restoring the parent brings those records and their history back. Restore the parent before writing to its attachments. The endpoint reference describes each request and response. These routes access text on a Story clip; they do not provide Story clip or timeline editing endpoints.