Skip to content
Hoody.com

The drag-and-drop endpoints inject a file (or a tree of files) into a running terminal session as a single OSC 8472 escape frame, the way a graphical terminal emulator would interpret a real OS drag-and-drop event. All four endpoints are scoped to a single terminal session and require that session to be a PTY session; non-PTY sessions are rejected with 409.

There are two ways to drop a file, and choosing the right one matters:

  • One-shot dropPOST /api/v1/terminal/drop performs begin, stage, and commit in a single request. Use this for small files that fit comfortably in one JSON body.
  • Staged transaction — for large files, or several files dropped together, run the three-step sequence in order: begin, upload (one or more raw-slice uploads), then commit.

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 that the terminal interprets as a drag-and-drop event. An uncommitted transaction stages bytes and changes nothing on screen.

The base URL for all endpoints is the per-container terminal subdomain:

https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.icu

Stages a small drop inline and commits it in a single request through the same server code paths as begin / upload / commit. The items array carries each file’s base64 content inline, or a nested directory tree via {name, dir: true, items: [...]} entries. PTY sessions only; this endpoint never blocks.

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 165535)
FieldTypeRequiredDescription
ctxstringYesDrop context: "drop" or "paste"
rintegerNoDrop cell row
cintegerNoDrop cell column
itemsarrayYesFile/dir items ([{name, b64}] for files, {name, dir: true, items: [...]} for nested directories)
Terminal window
curl -X POST \
"https://proj-acme-ctr-7f3a-terminal-1.us-east-1.containers.hoody.icu/api/v1/terminal/drop?terminal_id=12" \
-H "Content-Type: application/json" \
-d '{
"ctx": "drop",
"r": 4,
"c": 12,
"items": [
{
"name": "README.md",
"b64": "IyBSZWFkbWUKClRoaXMgaXMgYSBzYW1wbGUgZHJhZy1hbmQtZHJvcCBmaWxlLg=="
},
{
"name": "src",
"dir": true,
"items": [
{
"name": "index.js",
"b64": "Y29uc29sZS5sb2coJ2hlbGxvJyk7Cg=="
}
]
}
]
}'

Drop sealed and frame injected.

{}

For a large file, or several files dropped together, run these three calls in order, carrying the same drop id and token through all three:

  1. POST /api/v1/terminal/drop-begin — opens the transaction and returns the drop handle.
  2. POST /api/v1/terminal/upload — uploads one raw slice of bytes. Call repeatedly to stream a file slice by slice.
  3. POST /api/v1/terminal/drop-commit — finalizes the transaction and injects the OSC frame.

Mints a server-side drop id and a high-entropy drop token, and creates the staging directory for the terminal. The max_slice field in the response 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 165535)

This endpoint accepts no request body.

Terminal window
curl -X POST \
"https://proj-acme-ctr-7f3a-terminal-1.us-east-1.containers.hoody.icu/api/v1/terminal/drop-begin?terminal_id=12"

Drop transaction opened.

{
"drop": "dr_01HMZ3K9C5VQF0RJX8WT6E2N4B",
"token": "tk_8c4f1d2e9b0a47f6a3c1e9d4b5f7a2c8e1d4b6f9a3c2e7d1b4f8a5c9e2d6b3f1",
"max_slice": 262144
}

Appends 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). Call this endpoint repeatedly to stream a large file slice by slice.

  • offset MUST equal the file’s current staged size; otherwise 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 all return 409.
  • Byte/count caps return 413.
NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 165535)
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 application/octet-stream bytes. The request body schema is empty — pass the slice bytes directly, with no JSON wrapping.

Terminal window
# Upload slice #1 of "build.log" — bytes 0..262143 (offset must equal current staged size)
curl -X POST \
"https://proj-acme-ctr-7f3a-terminal-1.us-east-1.containers.hoody.icu/api/v1/terminal/upload?terminal_id=12&drop=dr_01HMZ3K9C5VQF0RJX8WT6E2N4B&token=tk_8c4f1d2e9b0a47f6a3c1e9d4b5f7a2c8e1d4b6f9a3c2e7d1b4f8a5c9e2d6b3f1&path=build.log&offset=0" \
-H "Content-Type: application/octet-stream" \
--data-binary @build.log.part1
# Upload slice #2 — offset is now the size of slice #1
curl -X POST \
"https://proj-acme-ctr-7f3a-terminal-1.us-east-1.containers.hoody.icu/api/v1/terminal/upload?terminal_id=12&drop=dr_01HMZ3K9C5VQF0RJX8WT6E2N4B&token=tk_8c4f1d2e9b0a47f6a3c1e9d4b5f7a2c8e1d4b6f9a3c2e7d1b4f8a5c9e2d6b3f1&path=build.log&offset=262144" \
-H "Content-Type: application/octet-stream" \
--data-binary @build.log.part2

Slice staged.

{}

Verifies 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), writes the canonical manifest.json, seals the drop, and injects 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 165535)
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 or oversized values are ignored.
itemsarrayYesManifest entries [{p, d, s, name, h?}]
Terminal window
curl -X POST \
"https://proj-acme-ctr-7f3a-terminal-1.us-east-1.containers.hoody.icu/api/v1/terminal/drop-commit?terminal_id=12&drop=dr_01HMZ3K9C5VQF0RJX8WT6E2N4B&token=tk_8c4f1d2e9b0a47f6a3c1e9d4b5f7a2c8e1d4b6f9a3c2e7d1b4f8a5c9e2d6b3f1" \
-H "Content-Type: application/json" \
-d '{
"ctx": "drop",
"r": 4,
"c": 12,
"cr": "clip_8f3a",
"items": [
{
"name": "build.log",
"p": "build.log",
"d": 0,
"s": 524288,
"h": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"name": "empty",
"p": "empty",
"d": 1
}
]
}'

Drop sealed and frame injected.

{}