Skip to content
Hoody.com

The Terminal Drag-and-Drop endpoints inject a file (or a tree of files) into a running PTY terminal session by staging the bytes on the server, sealing a JSON manifest, and emitting a single OSC 8472 escape frame on the terminal’s input. The program running inside the terminal then interprets that frame as a drag-and-drop event in the same way it would for a native file drop.

There are two ways to deliver a drop:

  1. One-shot dropPOST /api/v1/terminal/drop. Begin, stage, and commit in a single request. Use this for a small file that fits comfortably in one request body.
  2. Staged transaction — for a large file, or several files dropped together, run the three-step sequence in order:
    1. POST /api/v1/terminal/drop-begin — opens a staging transaction and returns the drop handle.
    2. POST /api/v1/terminal/upload — uploads one raw slice of file bytes. Call it repeatedly to stream a large file slice by slice.
    3. POST /api/v1/terminal/drop-commit — finalizes the transaction and injects the OSC frame.

The drop only becomes visible to the program running in the terminal at the commit step (or at the end of the one-shot call): committing is what injects the OSC escape frame. An uncommitted transaction only stages bytes and changes nothing on screen.

All four endpoints are PTY-only and operate on the container-scoped terminal host. Replace {projectId}-{containerId} with your project and container ids in every URL.

Begin, stage, and commit a small drop in a single request. Items are {name, b64} for files (base64 content) or {name, dir:true, items:[...]} for nested directories. PTY sessions only. Never blocks.

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535)
FieldTypeRequiredDescription
ctxstringYesDrop context: "drop" or "paste"
rintegerNoDrop cell row
cintegerNoDrop cell column
itemsarrayYesFile/dir items ([{name,b64} | {name,dir:true,items:[...]}])

Drop sealed and frame injected.

{}
Terminal window
curl -X POST "https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/drop?terminal_id=1" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"ctx": "drop",
"items": [
{ "name": "hello.txt", "b64": "aGVsbG8gd29ybGQ=" }
]
}'

Use the three-step sequence below for any drop that is too large to fit in one request body, or when you are streaming several files in one logical drop. Each step carries the same drop handle and token returned by /drop-begin. The drop becomes visible to the terminal at the commit step.

Step 1 — POST /api/v1/terminal/drop-begin

Section titled “Step 1 — POST /api/v1/terminal/drop-begin”

Mint a server-side drop id and a high-entropy drop token, and create the staging directory for the terminal. The client uploads file slices via /upload (authenticated by the token) and finalizes via /drop-commit. max_slice echoes the server’s effective request-body cap so the client never triggers a 413 mid-drop. Returns 409 when the terminal session has no exported staging base (e.g. staging disabled or a fail-closed spawn).

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535)

This endpoint takes no request body.

Drop transaction opened.

{}
Terminal window
curl -X POST "https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/drop-begin?terminal_id=1" \
-H "Authorization: Bearer <token>"

Append the raw request body to the staged file at path starting at byte offset. The body is not JSON — it is the raw slice bytes (up to the server’s --max-body-size). offset must equal the file’s current staged size, else the request returns 409 with the current size in the JSON error body so the client can resume. Missing or wrong token, an unknown drop, or a sealed drop return 409. Byte/count caps return 413.

Call this endpoint repeatedly to stream a large file slice by slice.

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535)
dropquerystringYesDrop id from /drop-begin
tokenquerystringYesDrop token from /drop-begin
pathquerystringYesSanitized relative path of the staged file (no .., not absolute)
offsetqueryintegerYesByte offset to write at (must equal the current staged size)

Raw file-slice bytes — sent as application/octet-stream, up to the server’s --max-body-size. The body is NOT JSON.

Slice staged.

{}
Terminal window
curl -X POST "https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/upload?terminal_id=1&drop={dropId}&token={dropToken}&path=hello.txt&offset=0" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/octet-stream" \
--data-binary @hello.txt

Step 3 — POST /api/v1/terminal/drop-commit

Section titled “Step 3 — POST /api/v1/terminal/drop-commit”

Verify the JSON manifest draft (items) against the staged bytes (existence, size, and sha256 h when present; zero-size items are created; d:1 entries are empty directories), write the canonical manifest.json, seal the drop, and inject one OSC 8472 frame into the terminal’s PTY input carrying the base64url staging root and the manifest sha256. PTY sessions only (else 409). A duplicate commit of an already-sealed drop with a valid token re-injects the same frame and returns 200 (idempotent retry rescue).

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535)
dropquerystringYesDrop id from /drop-begin
tokenquerystringYesDrop token from /drop-begin
FieldTypeRequiredDescription
ctxstringYesDrop context: "drop" or "paste"
rintegerNoDrop cell row (Chat grid pane mapping)
cintegerNoDrop cell column
crstringNoClip-read correlation nonce ([A-Za-z0-9_-]{1,64}); echoed verbatim as the injected frame’s cr field so the TUI can match a clipboard-read landing. Invalid/oversized values are ignored.
itemsarrayYesManifest entries ([{p,d,s,name,h?}])

Drop sealed and frame injected.

{}
Terminal window
curl -X POST "https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/drop-commit?terminal_id=1&drop={dropId}&token={dropToken}" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"ctx": "drop",
"items": [
{ "p": "hello.txt", "d": 0, "s": 11, "name": "hello.txt" }
]
}'