Run a command
Runs in the session's persistent shell (or a named one, or a fresh process with shell: false), after the session's setup commands finish. cd and export in a command carry to the next one (and across a stop and resume); cwd, env and credentials apply to this command only. Commands on one shell run one at a time. A command that times out is stopped with everything it started; the shell keeps its directory and exports, and background jobs keep running. Without stream, each of stdout and stderr keeps its first 64 KiB and last 448 KiB, with [… N characters not shown …] between them (truncated: true). With stream: true the reply is NDJSON as the command runs (nothing is cut); leaving stops the command. The session's exported credentials and the command's credentials are hidden in the output. 409 shell_disabled without a shell.
/v1/sessions/{id}/shell/execRequest
| Method and path | POST/v1/sessions/{id}/shell/exec |
|---|---|
| Base URL | https://api.boxline.dev |
| Authentication | Your API key in the x-api-key header (or Authorization: Bearer). The examples read it from BOXLINE_API_KEY. |
Path parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
idrequired | string | – | – |
Body
JSON with content-type: application/json. Required.
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
commandrequired | string | – | – | |
timeoutMs | integer | 120000 | – | From 100 to 3600000. |
cwd | string | – | – | The directory for this command only (inside the workspace); the shell stays where it was. |
env | object | – | – | Variables for this command only. |
credentials | string[] | – | – | Credentials as environment variables for this command only (scope "shell" or "all", or shell true; one per field, and boxline-otp for its passwords); hidden in the output. Up to 50 items. |
shell | string | boolean | – | – | "default" (the persistent shell), another name for a second persistent shell, or false for a fresh process. |
stream | boolean | false | – |
More examples:
{"command":"ls -la","cwd":"downloads","env":{"LC_ALL":"C"}}curl -X POST "https://api.boxline.dev/v1/sessions/$SESSION_ID/shell/exec" \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d '{"command": "python3 -V"}'import { Boxline } from "@boxline/sdk";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLconst result = await bx.sessions.exec("SESSION_ID", "python3 -V");console.log(result.stdout, result.exitCode);from boxline import Boxlinebx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URLresult = bx.sessions.exec("SESSION_ID", "python3 -V")print(result["stdout"], result["exitCode"])boxline exec $SESSION_ID -- "python3 -V"# exits with the command's own exit code (124 when it ran out of time)Responses
200 OK
The result, or the NDJSON stream with stream:true.
application/json: JSON.application/x-ndjson: Newline-delimited JSON (one object per line), sent as it happens.
Fields
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
stdout | string | – | – | |
stderr | string | – | – | |
exitCode | integer | null | – | – | |
durationMs | integer | – | – | |
timedOut | boolean | – | – | |
truncated | boolean | – | – |
{ "stdout": "…", "stderr": "…", "exitCode": 0, "durationMs": 0, "timedOut": false, "truncated": false}Errors
Errors have a matching HTTP status and the same body. code is stable, so check it in your code; message is for people. See Errors and request ids.
| Status | Codes | When |
|---|---|---|
| 400 Bad Request | invalid_request, invalid_json, invalid_cursor, invalid_idempotency_key, invalid_extension, variables_with_extensions, terms_not_accepted, invalid_reset_token, invalid_confirmation_code, wrong_password, no_password, invalid_next, invalid_oauth_token | The request is not valid. |
| 401 Unauthorized | unauthorized | Missing or invalid API key. |
| 404 Not Found | not_found, invite_not_found, provider_unavailable | No such thing in this project (or organization), or no such route. |
| 409 Conflict | session_not_running, shell_disabled, browser_disabled, nothing_saved, not_resumable, invalid_state, conflict, email_taken, identity_in_use, provider_already_linked, last_sign_in_method, not_impersonating, machine_too_old, too_many_credential_values, limit_reached, terms_version_mismatch, no_project, last_owner, organization_not_empty, project_has_sessions, already_member, invite_pending, idempotency_in_progress | The current state does not allow it, or a request with this Idempotency-Key is still being answered. |
| 502 Bad Gateway | machine_error, machine_unreachable, page_unreachable | The session machine failed or could not be reached, or, for fetch, screenshot, pdf and extract, the page could not be loaded. |
{ "error": { "code": "not_found", "message": "session not found", "requestId": "req_4fJx0aQm2Lk9Pz7N" }}Use with the browser
Read what the shell wrote after it worked on a download. See Browser, shell and files together.
curl -G https://api.boxline.dev/v1/sessions/$SESSION_ID/files/wait \ -H "x-api-key: $BOXLINE_API_KEY" \ --data-urlencode "pattern=downloads/*.csv"curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/shell/exec \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d @- <<'JSON'{"command": "mkdir -p output && python3 -c \"import pandas as pd; print(pd.read_csv('downloads/report.csv')['revenue'].sum())\" > output/total.txt"}JSONcurl -G https://api.boxline.dev/v1/sessions/$SESSION_ID/files \ -H "x-api-key: $BOXLINE_API_KEY" \ --data-urlencode "path=output/total.txt"const csv = await session.files.waitFor("downloads/*.csv"); // the browser's downloadawait session.exec(`mkdir -p output && python3 -c "import pandas as pd; print(pd.read_csv('downloads/report.csv')['revenue'].sum())" > output/total.txt`);const total = (await session.files.readText("output/total.txt")).trim();csv = session.files.wait_for("downloads/*.csv") # the browser's downloadsession.exec("""mkdir -p output && python3 -c "import pandas as pd; print(pd.read_csv('downloads/report.csv')['revenue'].sum())" > output/total.txt""")total = session.files.read_text("output/total.txt").strip()Learn more
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.