Clipboard API
client.clipboard reads and replaces the system clipboard in three separate formats — text, images and files — each behind its own permission, so an app asks only for what it needs.
Availability
| Operation | Permission | Notes |
|---|---|---|
readText | system.clipboard.read | Reads content put there by any application. |
writeText | system.clipboard.write | Replaces the clipboard. |
readImage | system.clipboard.image.read | RGBA pixels. |
writeImage | system.clipboard.image.write | RGBA pixels. |
copyFiles, cutFile | system.clipboard.files.write, plus files.download (copy) or files.move (cut) | Windows, with a connected workspace. |
pasteFiles | system.clipboard.files.read and files.upload | Windows. |
Text and image access work in any workspace, including the local desktop. File clipboard operations need a connected file service and the native file clipboard, which today means Windows.
Interface
interface ClipboardImage { width: number; height: number; rgba: Uint8Array } // row-major, top to bottom
interface AppClipboardAPI {
readText(signal?: AbortSignal): Promise<string>;
writeText(text: string, signal?: AbortSignal): Promise<void>;
readImage(signal?: AbortSignal): Promise<ClipboardImage>;
writeImage(image: ClipboardImage, signal?: AbortSignal): Promise<void>;
copyFiles(entries: RemoteEntryLocation[], signal?: AbortSignal): Promise<void>;
cutFile(entry: RemoteEntryLocation, signal?: AbortSignal): Promise<void>;
pasteFiles(destination: RemoteFileLocation, signal?: AbortSignal): Promise<RemoteTransfer[]>;
}Text is limited to 4 Mi UTF-16 units and images to 64 MiB of RGBA data. Both are streamed in chunks by the SDK, so your code sees one call.
Examples
Text, in both directions:
const text = await client.clipboard.readText();
await client.clipboard.writeText(`${text}\n— copied from My Notes`);An image, drawn into a canvas:
const image = await client.clipboard.readImage();
const canvas = new OffscreenCanvas(image.width, image.height);
const context = canvas.getContext("2d")!;
context.putImageData(new ImageData(new Uint8ClampedArray(image.rgba), image.width, image.height), 0, 0);Writing one back requires exactly width * height * 4 bytes:
await client.clipboard.writeImage({ width, height, rgba });Publishing remote files so the user can paste them into their file manager, then pasting files in:
await client.clipboard.copyFiles(selection.map((e) => ({ binding, path: e.path, revision: e.revision! })));
const transfers = await client.clipboard.pasteFiles({ binding, path: currentFolder.path });
for (const transfer of transfers) {
try { await transfer.run(); } finally { await transfer.close(); }
}pasteFiles returns transfers, so progress and cancellation work exactly as on the Transfers API page. When the clipboard holds no files it resolves to an empty array rather than failing.
Errors
| Code | Message | Meaning |
|---|---|---|
invalid | Clipboard text must be a string. | Wrong type. |
invalid | Clipboard text must not exceed 4 Mi UTF-16 units. | Too much text. |
invalid | Provide positive image dimensions with a representable RGBA length. | Dimensions and buffer disagree. |
invalid | Cut a current file entry from an accepted workspace binding. | Stale or foreign entry. |
invalid | Copy current file entries from one workspace binding. | Entries from several connections. |
busy | A clipboard read is already active in this window. | One read at a time. |
busy | Finish or cancel the previous clipboard publication first. | A file publication is still running. |
unavailable | Native file clipboard export is unavailable. | Not Windows, or no native clipboard. |
aborted | Clipboard publication ended after cancellation or a connection change. It may have completed; do not retry automatically. | Genuinely uncertain. |
failed | Unable to read clipboard text. / Unable to replace clipboard text. | The OS refused. |
Lifecycle and permissions
One read and one write may be active per window; a second call fails with busy rather than interleaving. Reads and writes take a snapshot before any awaiting, so a clipboard change mid-transfer cannot splice two contents together, and an image is published only when complete.
Publishing files to the system clipboard is not instant: the desktop scans the selection first, and the offer stays valid only while ShellCanvas and the connection remain open — the bytes stream when the user pastes. A cancellation cannot retract an offer the operating system has already accepted.
Remember what these permissions mean to a user: clipboard read sees whatever they last copied anywhere, including passwords. Ask for the narrowest format you need, and read only in response to a user action.
Related guides
- Transfers API — the handles
pasteFilesreturns. - Files API — entries, revisions and bindings.
- Upload, download and copy — the Explorer integration users see.
- Extension permissions and trust — how these grants are described at install time.