Add documents, sheets, files, and sessions
Create or reuse channel content in the app, CLI, API, SDK, or MCP without asking an agent to generate it.
You can write a document, build a sheet, or upload an ordinary file directly. An MLX agent does not need to generate it. The same content can be referenced from a Channel or a Data Set record.
Add content in a Channel
Open a Channel and choose Add:
- Document creates an editable text artifact.
- Sheet creates an editable CSV sheet.
- Upload files uploads files from your computer.
- Add existing… finds an accessible artifact, file, or session to attach.
- Start session opens a new conversation with the Channel as its context.
Documents and sheets are artifacts with versions. Uploads retain their original file format. Use a sheet for an editable grid, or upload a spreadsheet when you want to preserve the original workbook.
Removing an existing item from a Channel removes that attachment. It does not delete the original content. Content belonging to the Channel itself, or shown through session activity, can have other reasons to remain visible.
Use content in a record field
Add a field to a Data Set and choose File, Artifact (document or sheet), or Session. The field stores the existing resource's ID and displays a link you can open. You can choose an existing item, or create content from the field picker. Clearing the field removes its value; it does not delete the resource.
A Session field points to a conversation. Selecting one does not start a run. A field reference does not grant access to its resource: people opening the record must also have access to the file, artifact, or session.
For records projected from a connected source, new content is placed in the known Channel and its ID is saved in the editable record field. The separate record attachment API accepts stored workspace record IDs; a source-system row ID alone does not identify a workspace attachment destination.
Use the CLI
Install and sign in to the CLI, then use the same commands from your terminal or a local coding assistant:
twin login
# Create a document or sheet directly from local content.
twin artifacts --kind markdown --title "Project brief" \
--file brief.md --channel operations --allow-write
twin artifacts --kind csv --title "Delivery plan" \
--file plan.csv --channel operations --allow-write
# Upload original bytes and place the file in the Channel.
twin files --upload proposal.pdf --channel operations --allow-write
# Find existing content and download an uploaded file.
twin files --q proposal --json
twin artifacts --channel operations --json
twin sessions --json
twin files --file FILE_ID --download proposal-copy.pdf
# Attach existing resources without copying them.
twin files --file FILE_ID --attach --channel operations --allow-write
twin artifacts --artifact ARTIFACT_ID --attach --channel operations --allow-write
twin sessions --session SESSION_ID --attach --channel operations --allow-write
# Remove one attachment without deleting the original.
twin files --file FILE_ID --detach --channel operations --allow-write
twin artifacts --artifact ARTIFACT_ID --detach --channel operations --allow-write
twin sessions --session SESSION_ID --detach --channel operations --allow-writeFile attachment also accepts --session SESSION_ID or --record RECORD_ID in
place of --channel. Artifact attachment accepts --record RECORD_ID. A Session
field is set through the record API; session attachment targets Channels.
Use twin help files, twin help artifacts, or twin help sessions for all
options. Writes require --allow-write. Downloading to an existing local path
fails instead of overwriting it.
Use the HTTP API or TypeScript SDK
Public clients use the Twin gateway with their OAuth bearer token or signed MLX
subject token. Reads require twin.read; changes require twin.write and the
user's normal resource permissions.
The upload endpoint accepts real multipart file bytes, up to 50 MiB or the lower limit configured by your organisation. Let your HTTP client generate the multipart boundary:
curl "$TWIN_GATEWAY/api/twin/files" \
-H "Authorization: Bearer $TWIN_ACCESS_TOKEN" \
-F "file=@proposal.pdf" \
-F "channelId=CHANNEL_ID"The response contains file.id. Use that ID to retrieve metadata at
GET /api/twin/files/FILE_ID, download bytes at
GET /api/twin/files/FILE_ID/content, or attach it elsewhere:
POST /api/twin/files/FILE_ID/links
Content-Type: application/json
Authorization: Bearer <access-token>
{"targetType":"record","targetId":"RECORD_ID"}Use DELETE at the same path with the same body to detach. Artifact references
use /api/twin/artifacts/ARTIFACT_ID/links and accept channel or record.
Session sharing uses /api/twin/sessions/SESSION_ID/channels with
{"channelId":"CHANNEL_ID"}. Artifact and session attachment responses contain attached; their removal
responses contain detached. false means the requested state already held.
File attachment and removal return the updated file with its current links.
The handwritten TypeScript SDK exposes the same operations:
import { SDK } from "@mercury-labs/mlx-agent-sdk";
const sdk = new SDK({
serverURL: process.env.TWIN_GATEWAY,
bearerToken: process.env.TWIN_ACCESS_TOKEN,
});
const { file } = await sdk.twin.uploadTwinFile({
file: new File(["Draft brief"], "brief.txt", { type: "text/plain" }),
channelId: "CHANNEL_ID",
});
await sdk.twin.attachTwinFile({
fileId: file.id, targetType: "record", targetId: "RECORD_ID",
});
await sdk.twin.attachTwinArtifact({
artifactId: "ARTIFACT_ID", targetType: "channel", targetId: "CHANNEL_ID",
});
await sdk.twin.attachTwinSession({ sessionId: "SESSION_ID", channelId: "CHANNEL_ID" });Create documents and sheets with sdk.twin.createTwinArtifact, using kind: "markdown" or kind: "csv", title, content, an idempotencyKey, and an
optional channelId. Updating an artifact requires its expectedVersionId to
avoid replacing someone else's newer version.
Use MCP
Connected MCP clients have the same resource operations:
| Action | MCP tools |
|---|---|
| Find or inspect files | twin_files_list, twin_file_get |
| Upload a small file | twin_file_upload |
| Prepare a larger upload | twin_file_upload_prepare |
| Read a file | twin_file_content |
| Attach or detach a file | twin_file_attach, twin_file_detach |
| Create or edit an artifact | twin_artifact_create, twin_artifact_update |
| Attach or detach an artifact | twin_artifact_attach, twin_artifact_detach |
| Share or unshare a session | twin_session_attach, twin_session_detach |
twin_file_upload accepts a filename, content type, and base64 content with a
maximum decoded size of 32 KiB. It performs the upload and returns the saved
file. For example:
{"fileName":"note.txt","contentType":"text/plain","contentBase64":"SGVsbG8=","channelId":"CHANNEL_ID"}For a larger file, call twin_file_upload_prepare with its placement, such as
{"channelId":"CHANNEL_ID"}. It returns uploadUrl, method, fileField,
fields, maxBytes, and authentication instructions. This response says
uploaded: false: preparation has not uploaded a file. The client must then
send the actual multipart request with the same authenticated authority and
check the upload response. An MCP client that cannot send authenticated HTTP
uploads needs the CLI or another HTTP client for files larger than 32 KiB.
Understand access when adding existing content
| Resource | What adding it to a Channel does |
|---|---|
| File | Grants access through the destination. Only its creator or an organisation admin can change its attachments. |
| Artifact | Adds a reference to the same artifact and versions. Its original access rules remain in force. |
| Session | Explicitly grants current Channel readers access to the conversation. Only the session owner or an organisation admin can share it. |
Session sharing does not transfer ownership, grant permission to send messages or start runs, or share its task, artifacts, or files. Those keep their own access rules. Removing the explicit session attachment revokes that grant; independent access and actual Channel activity remain. An agent merely reading or writing a Channel does not create a session sharing grant.