Skip to content
Hoody.com

These endpoints stage files into a terminal session and inject them as a drag-and-drop frame. There are two ways to deliver a drop: a single one-shot request that stages and commits inline, and a three-step staged transaction that streams large files slice-by-slice before commit. Both paths end at the same final action: writing an OSC 8472 escape frame into the terminal’s PTY input. A drop only becomes visible to the program running in the terminal at the commit step (or at the end of the one-shot call). An uncommitted staged transaction holds bytes on the server but changes nothing on screen.

Use the one-shot drop when…Use the staged transaction when…
The file is small enough to base64-encode into a single JSON request bodyThe file is large or you want to stream it in slices
You want the simplest call shape (one request, one response)You need to coordinate multiple files dropped together
Latency of a single round-trip matters more than throughputYou want the server to tell you its effective max_slice cap before you start uploading

POST /api/v1/terminal/drop runs the begin, upload, and commit code paths in one request. Items are encoded as {name, b64} for files or {name, dir: true, items: [...]} for nested directories, with file contents base64-encoded in-line. Use this for small drops where the JSON body comfortably fits one HTTP request.

Begin, stage, and commit a drop in a single request through the same server code paths as the staged sequence. Intended for small drops and end-to-end tests. PTY sessions only. Never blocks.

Parameters

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535). On calls routed through a terminal-N containers-proxy hostname, the proxy sets this from the hostname label and overwrites any value you send — pick the terminal via the hostname; supply it directly only when calling the terminal service without the proxy

Request Body

FieldTypeRequiredDescription
ctxstringYesDrop context: "drop" or "paste"
rintegerNoDrop cell row
cintegerNoDrop cell column
itemsarrayYesFile/dir items ([{name, b64}] or [{name, dir: true, items: [...]}])
{
"ctx": "drop",
"r": 12,
"c": 4,
"items": [
{
"name": "report.pdf",
"b64": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9MZW5ndGggMjE0Pj5zdHJlYW0KQlQKL1YxIDIgMCBSPj4+CmVuZG9iago="
},
{
"name": "attachments",
"dir": true,
"items": [
{
"name": "notes.md",
"b64": "IyBNb250aGx5IFJlcG9ydAoKVGhpcyBpcyBhIHNhbXBsZSByZXBvcnQuCg=="
}
]
}
]
}

Response

Drop sealed and frame injected.

{
"status": "ok",
"drop_id": "A1b2C3d4E5f6G7h8I9j0Kl",
"mh": "<sha256hex>",
"n": 2
}

SDK Usage

Terminal window
curl -X POST "https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/drop?terminal_id=1" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ctx": "drop",
"r": 12,
"c": 4,
"items": [
{
"name": "report.pdf",
"b64": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9MZW5ndGggMjE0Pj5zdHJlYW0KQlQKL1YxIDIgMCBSPj4+CmVuZG9iago="
}
]
}'

For a large file or several files dropped together, run the three-step sequence in order. Each step carries the same drop handle and token returned by the begin call.

  1. Begin opens a staging transaction and returns the drop handle plus max_slice, the server’s effective per-slice body cap.
  2. Upload appends one raw slice of file bytes at a given offset. Call it repeatedly to stream a large file.
  3. Commit finalizes the transaction: verifies the manifest against the staged bytes, writes manifest.json, seals the drop, and injects one OSC 8472 frame into the terminal.

Bytes accumulated during the upload phase are invisible to the program in the terminal until the commit step runs. A duplicate commit of an already-sealed drop with a valid token re-injects the same frame and returns 200.

Mints a server-side drop id and a high-entropy drop token, and creates 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 (for example, staging disabled or a fail-closed spawn).

Parameters

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535). On calls routed through a terminal-N containers-proxy hostname, the proxy sets this from the hostname label and overwrites any value you send — pick the terminal via the hostname; supply it directly only when calling the terminal service without the proxy

This endpoint accepts no request body.

Response

Drop transaction opened. The body returns the drop id, drop token, and max_slice cap.

{
"drop_id": "A1b2C3d4E5f6G7h8I9j0Kl",
"drop_token": "...",
"max_slice": 8388608
}

SDK Usage

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 $HOODY_TOKEN"

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, at most max_slice bytes as returned by drop-begin. 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 and count caps return 413.

Parameters

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535). On calls routed through a terminal-N containers-proxy hostname, the proxy sets this from the hostname label and overwrites any value you send — pick the terminal via the hostname; supply it directly only when calling the terminal service without the proxy
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)

Request Body

Raw file-slice bytes (NOT JSON) sent as application/octet-stream, at most max_slice bytes as returned by drop-begin.

Response

Slice staged.

{
"status": "ok",
"bytes_written": 4096,
"offset": 8192
}

SDK Usage

Terminal window
curl -X POST "https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/upload?terminal_id=1&drop=drp_a1b2c3d4e5f67890&token=tkn_zXk7pQ2vN9mL4jR8sY5wA1b2C3&path=large.bin&offset=0" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @slice-001.bin

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 (otherwise 409). A duplicate commit of an already-sealed drop with a valid token re-injects the same frame and returns 200 (idempotent retry rescue).

Parameters

NameInTypeRequiredDescription
terminal_idquerystringYesTerminal session ID (numeric 1-65535). On calls routed through a terminal-N containers-proxy hostname, the proxy sets this from the hostname label and overwrites any value you send — pick the terminal via the hostname; supply it directly only when calling the terminal service without the proxy
dropquerystringYesDrop id from /drop-begin
tokenquerystringYesDrop token from /drop-begin

Request Body

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?}]
{
"ctx": "drop",
"r": 12,
"c": 4,
"cr": "clip-abc123",
"items": [
{ "p": "large.bin", "d": 0, "s": 1048576, "name": "large.bin" }
]
}

Response

Drop sealed and frame injected.

{
"status": "ok",
"drop_id": "A1b2C3d4E5f6G7h8I9j0Kl",
"mh": "<sha256hex>",
"n": 3
}

SDK Usage

Terminal window
curl -X POST "https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com/api/v1/terminal/drop-commit?terminal_id=1&drop=drp_a1b2c3d4e5f67890&token=tkn_zXk7pQ2vN9mL4jR8sY5wA1b2C3" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ctx": "drop",
"r": 12,
"c": 4,
"cr": "clip-abc123",
"items": [
{ "p": "large.bin", "d": 0, "s": 1048576, "name": "large.bin" }
]
}'

The three staged calls share the same drop handle and token returned by drop-begin. The slice upload in step 2 is repeated once per slice; the example below shows a single 1 MiB slice for clarity.

Terminal window
HOST="https://{projectId}-{containerId}-terminal-1.{server}.containers.hoody.com"
AUTH="Authorization: Bearer $HOODY_TOKEN"
# 1. Begin
BEGIN=$(curl -s -X POST "$HOST/api/v1/terminal/drop-begin?terminal_id=1" -H "$AUTH")
DROP=$(echo "$BEGIN" | jq -r '.drop_id')
TOKEN=$(echo "$BEGIN" | jq -r '.drop_token')
MAX_SLICE=$(echo "$BEGIN" | jq -r '.max_slice')
# 2. Upload one slice
curl -X POST "$HOST/api/v1/terminal/upload?terminal_id=1&drop=$DROP&token=$TOKEN&path=large.bin&offset=0" \
-H "$AUTH" \
-H "Content-Type: application/octet-stream" \
--data-binary @slice-001.bin
# 3. Commit
curl -X POST "$HOST/api/v1/terminal/drop-commit?terminal_id=1&drop=$DROP&token=$TOKEN" \
-H "$AUTH" \
-H "Content-Type: application/json" \
-d '{
"ctx": "drop",
"r": 12,
"c": 4,
"items": [
{ "p": "large.bin", "d": 0, "s": 1048576, "name": "large.bin" }
]
}'

For a multi-slice file, repeat step 2 with offset set to the cumulative byte count already written. The 409 error body on an offset mismatch reports the current staged size so you can resume without re-uploading from the beginning.